diff options
Diffstat (limited to 'src/engine')
78 files changed, 41422 insertions, 0 deletions
diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp new file mode 100644 index 00000000..eda5fc64 --- /dev/null +++ b/src/engine/EngineForApplication.cpp | |||
| @@ -0,0 +1,894 @@ | |||
| 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 | |||
| 5 | static HRESULT BAEngineGetPackageCount( | ||
| 6 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 7 | __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/, | ||
| 8 | __in BAENGINE_GETPACKAGECOUNT_RESULTS* pResults | ||
| 9 | ) | ||
| 10 | { | ||
| 11 | HRESULT hr = S_OK; | ||
| 12 | DWORD* pcPackages = &pResults->cPackages; | ||
| 13 | |||
| 14 | *pcPackages = pContext->pEngineState->packages.cPackages; | ||
| 15 | |||
| 16 | return hr; | ||
| 17 | } | ||
| 18 | |||
| 19 | static HRESULT BAEngineGetVariableNumeric( | ||
| 20 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 21 | __in BAENGINE_GETVARIABLENUMERIC_ARGS* pArgs, | ||
| 22 | __in BAENGINE_GETVARIABLENUMERIC_RESULTS* pResults | ||
| 23 | ) | ||
| 24 | { | ||
| 25 | HRESULT hr = S_OK; | ||
| 26 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 27 | LONGLONG* pllValue = &pResults->llValue; | ||
| 28 | |||
| 29 | if (wzVariable && *wzVariable) | ||
| 30 | { | ||
| 31 | hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue); | ||
| 32 | } | ||
| 33 | else | ||
| 34 | { | ||
| 35 | hr = E_INVALIDARG; | ||
| 36 | } | ||
| 37 | |||
| 38 | return hr; | ||
| 39 | } | ||
| 40 | |||
| 41 | static HRESULT BAEngineGetVariableString( | ||
| 42 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 43 | __in BAENGINE_GETVARIABLESTRING_ARGS* pArgs, | ||
| 44 | __in BAENGINE_GETVARIABLESTRING_RESULTS* pResults | ||
| 45 | ) | ||
| 46 | { | ||
| 47 | HRESULT hr = S_OK; | ||
| 48 | LPWSTR sczValue = NULL; | ||
| 49 | size_t cchRemaining = 0; | ||
| 50 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 51 | LPWSTR wzValue = pResults->wzValue; | ||
| 52 | DWORD* pcchValue = &pResults->cchValue; | ||
| 53 | |||
| 54 | if (wzVariable && *wzVariable) | ||
| 55 | { | ||
| 56 | hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue); | ||
| 57 | if (SUCCEEDED(hr)) | ||
| 58 | { | ||
| 59 | if (wzValue) | ||
| 60 | { | ||
| 61 | hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); | ||
| 62 | if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) | ||
| 63 | { | ||
| 64 | hr = E_MOREDATA; | ||
| 65 | |||
| 66 | ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); | ||
| 67 | *pcchValue = cchRemaining + 1; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | else | ||
| 71 | { | ||
| 72 | hr = E_MOREDATA; | ||
| 73 | |||
| 74 | ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); | ||
| 75 | *pcchValue = cchRemaining + 1; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | else | ||
| 80 | { | ||
| 81 | hr = E_INVALIDARG; | ||
| 82 | } | ||
| 83 | |||
| 84 | StrSecureZeroFreeString(sczValue); | ||
| 85 | return hr; | ||
| 86 | } | ||
| 87 | |||
| 88 | static HRESULT BAEngineGetVariableVersion( | ||
| 89 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 90 | __in BAENGINE_GETVARIABLEVERSION_ARGS* pArgs, | ||
| 91 | __in BAENGINE_GETVARIABLEVERSION_RESULTS* pResults | ||
| 92 | ) | ||
| 93 | { | ||
| 94 | HRESULT hr = S_OK; | ||
| 95 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 96 | DWORD64* pqwValue = &pResults->qwValue; | ||
| 97 | |||
| 98 | if (wzVariable && *wzVariable) | ||
| 99 | { | ||
| 100 | hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue); | ||
| 101 | } | ||
| 102 | else | ||
| 103 | { | ||
| 104 | hr = E_INVALIDARG; | ||
| 105 | } | ||
| 106 | |||
| 107 | return hr; | ||
| 108 | } | ||
| 109 | |||
| 110 | static HRESULT BAEngineFormatString( | ||
| 111 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 112 | __in BAENGINE_FORMATSTRING_ARGS* pArgs, | ||
| 113 | __in BAENGINE_FORMATSTRING_RESULTS* pResults | ||
| 114 | ) | ||
| 115 | { | ||
| 116 | HRESULT hr = S_OK; | ||
| 117 | LPWSTR sczValue = NULL; | ||
| 118 | DWORD cchValue = 0; | ||
| 119 | LPCWSTR wzIn = pArgs->wzIn; | ||
| 120 | LPWSTR wzOut = pResults->wzOut; | ||
| 121 | DWORD* pcchOut = &pResults->cchOut; | ||
| 122 | |||
| 123 | if (wzIn && *wzIn) | ||
| 124 | { | ||
| 125 | hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue); | ||
| 126 | if (SUCCEEDED(hr)) | ||
| 127 | { | ||
| 128 | if (wzOut) | ||
| 129 | { | ||
| 130 | hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); | ||
| 131 | if (FAILED(hr)) | ||
| 132 | { | ||
| 133 | *pcchOut = cchValue; | ||
| 134 | if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) | ||
| 135 | { | ||
| 136 | hr = E_MOREDATA; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | else | ||
| 141 | { | ||
| 142 | hr = E_MOREDATA; | ||
| 143 | *pcchOut = cchValue; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | else | ||
| 148 | { | ||
| 149 | hr = E_INVALIDARG; | ||
| 150 | } | ||
| 151 | |||
| 152 | StrSecureZeroFreeString(sczValue); | ||
| 153 | return hr; | ||
| 154 | } | ||
| 155 | |||
| 156 | static HRESULT BAEngineEscapeString( | ||
| 157 | __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, | ||
| 158 | __in BAENGINE_ESCAPESTRING_ARGS* pArgs, | ||
| 159 | __in BAENGINE_ESCAPESTRING_RESULTS* pResults | ||
| 160 | ) | ||
| 161 | { | ||
| 162 | HRESULT hr = S_OK; | ||
| 163 | LPWSTR sczValue = NULL; | ||
| 164 | size_t cchRemaining = 0; | ||
| 165 | LPCWSTR wzIn = pArgs->wzIn; | ||
| 166 | LPWSTR wzOut = pResults->wzOut; | ||
| 167 | DWORD* pcchOut = &pResults->cchOut; | ||
| 168 | |||
| 169 | if (wzIn && *wzIn) | ||
| 170 | { | ||
| 171 | hr = VariableEscapeString(wzIn, &sczValue); | ||
| 172 | if (SUCCEEDED(hr)) | ||
| 173 | { | ||
| 174 | if (wzOut) | ||
| 175 | { | ||
| 176 | hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL); | ||
| 177 | if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) | ||
| 178 | { | ||
| 179 | hr = E_MOREDATA; | ||
| 180 | ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); | ||
| 181 | *pcchOut = cchRemaining; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | else | ||
| 185 | { | ||
| 186 | ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining); | ||
| 187 | *pcchOut = cchRemaining; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | else | ||
| 192 | { | ||
| 193 | hr = E_INVALIDARG; | ||
| 194 | } | ||
| 195 | |||
| 196 | StrSecureZeroFreeString(sczValue); | ||
| 197 | return hr; | ||
| 198 | } | ||
| 199 | |||
| 200 | static HRESULT BAEngineEvaluateCondition( | ||
| 201 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 202 | __in BAENGINE_EVALUATECONDITION_ARGS* pArgs, | ||
| 203 | __in BAENGINE_EVALUATECONDITION_RESULTS* pResults | ||
| 204 | ) | ||
| 205 | { | ||
| 206 | HRESULT hr = S_OK; | ||
| 207 | LPCWSTR wzCondition = pArgs->wzCondition; | ||
| 208 | BOOL* pf = &pResults->f; | ||
| 209 | |||
| 210 | if (wzCondition && *wzCondition) | ||
| 211 | { | ||
| 212 | hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf); | ||
| 213 | } | ||
| 214 | else | ||
| 215 | { | ||
| 216 | hr = E_INVALIDARG; | ||
| 217 | } | ||
| 218 | |||
| 219 | return hr; | ||
| 220 | } | ||
| 221 | |||
| 222 | static HRESULT BAEngineLog( | ||
| 223 | __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/, | ||
| 224 | __in BAENGINE_LOG_ARGS* pArgs, | ||
| 225 | __in BAENGINE_LOG_RESULTS* /*pResults*/ | ||
| 226 | ) | ||
| 227 | { | ||
| 228 | HRESULT hr = S_OK; | ||
| 229 | REPORT_LEVEL rl = REPORT_NONE; | ||
| 230 | BOOTSTRAPPER_LOG_LEVEL level = pArgs->level; | ||
| 231 | LPCWSTR wzMessage = pArgs->wzMessage; | ||
| 232 | |||
| 233 | switch (level) | ||
| 234 | { | ||
| 235 | case BOOTSTRAPPER_LOG_LEVEL_STANDARD: | ||
| 236 | rl = REPORT_STANDARD; | ||
| 237 | break; | ||
| 238 | |||
| 239 | case BOOTSTRAPPER_LOG_LEVEL_VERBOSE: | ||
| 240 | rl = REPORT_VERBOSE; | ||
| 241 | break; | ||
| 242 | |||
| 243 | case BOOTSTRAPPER_LOG_LEVEL_DEBUG: | ||
| 244 | rl = REPORT_DEBUG; | ||
| 245 | break; | ||
| 246 | |||
| 247 | case BOOTSTRAPPER_LOG_LEVEL_ERROR: | ||
| 248 | rl = REPORT_ERROR; | ||
| 249 | break; | ||
| 250 | |||
| 251 | default: | ||
| 252 | ExitFunction1(hr = E_INVALIDARG); | ||
| 253 | } | ||
| 254 | |||
| 255 | hr = LogStringLine(rl, "%ls", wzMessage); | ||
| 256 | ExitOnFailure(hr, "Failed to log BA message."); | ||
| 257 | |||
| 258 | LExit: | ||
| 259 | return hr; | ||
| 260 | } | ||
| 261 | |||
| 262 | static HRESULT BAEngineSendEmbeddedError( | ||
| 263 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 264 | __in BAENGINE_SENDEMBEDDEDERROR_ARGS* pArgs, | ||
| 265 | __in BAENGINE_SENDEMBEDDEDERROR_RESULTS* pResults | ||
| 266 | ) | ||
| 267 | { | ||
| 268 | HRESULT hr = S_OK; | ||
| 269 | BYTE* pbData = NULL; | ||
| 270 | DWORD cbData = 0; | ||
| 271 | DWORD dwResult = 0; | ||
| 272 | DWORD dwErrorCode = pArgs->dwErrorCode; | ||
| 273 | LPCWSTR wzMessage = pArgs->wzMessage; | ||
| 274 | DWORD dwUIHint = pArgs->dwUIHint; | ||
| 275 | int* pnResult = &pResults->nResult; | ||
| 276 | |||
| 277 | if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) | ||
| 278 | { | ||
| 279 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); | ||
| 280 | ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode."); | ||
| 281 | } | ||
| 282 | |||
| 283 | hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode); | ||
| 284 | ExitOnFailure(hr, "Failed to write error code to message buffer."); | ||
| 285 | |||
| 286 | hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L""); | ||
| 287 | ExitOnFailure(hr, "Failed to write message string to message buffer."); | ||
| 288 | |||
| 289 | hr = BuffWriteNumber(&pbData, &cbData, dwUIHint); | ||
| 290 | ExitOnFailure(hr, "Failed to write UI hint to message buffer."); | ||
| 291 | |||
| 292 | hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult); | ||
| 293 | ExitOnFailure(hr, "Failed to send embedded message over pipe."); | ||
| 294 | |||
| 295 | *pnResult = static_cast<int>(dwResult); | ||
| 296 | |||
| 297 | LExit: | ||
| 298 | ReleaseBuffer(pbData); | ||
| 299 | return hr; | ||
| 300 | } | ||
| 301 | |||
| 302 | static HRESULT BAEngineSendEmbeddedProgress( | ||
| 303 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 304 | __in BAENGINE_SENDEMBEDDEDPROGRESS_ARGS* pArgs, | ||
| 305 | __in BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS* pResults | ||
| 306 | ) | ||
| 307 | { | ||
| 308 | HRESULT hr = S_OK; | ||
| 309 | BYTE* pbData = NULL; | ||
| 310 | DWORD cbData = 0; | ||
| 311 | DWORD dwResult = 0; | ||
| 312 | DWORD dwProgressPercentage = pArgs->dwProgressPercentage; | ||
| 313 | DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage; | ||
| 314 | int* pnResult = &pResults->nResult; | ||
| 315 | |||
| 316 | if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode) | ||
| 317 | { | ||
| 318 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); | ||
| 319 | ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode."); | ||
| 320 | } | ||
| 321 | |||
| 322 | hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage); | ||
| 323 | ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); | ||
| 324 | |||
| 325 | hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage); | ||
| 326 | ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer."); | ||
| 327 | |||
| 328 | hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult); | ||
| 329 | ExitOnFailure(hr, "Failed to send embedded progress message over pipe."); | ||
| 330 | |||
| 331 | *pnResult = static_cast<int>(dwResult); | ||
| 332 | |||
| 333 | LExit: | ||
| 334 | ReleaseBuffer(pbData); | ||
| 335 | return hr; | ||
| 336 | } | ||
| 337 | |||
| 338 | static HRESULT BAEngineSetUpdate( | ||
| 339 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 340 | __in const BAENGINE_SETUPDATE_ARGS* pArgs, | ||
| 341 | __in BAENGINE_SETUPDATE_RESULTS* /*pResults*/ | ||
| 342 | ) | ||
| 343 | { | ||
| 344 | HRESULT hr = S_OK; | ||
| 345 | LPCWSTR sczId = NULL; | ||
| 346 | LPWSTR sczLocalSource = NULL; | ||
| 347 | LPWSTR sczCommandline = NULL; | ||
| 348 | UUID guid = { }; | ||
| 349 | WCHAR wzGuid[39]; | ||
| 350 | RPC_STATUS rs = RPC_S_OK; | ||
| 351 | LPCWSTR wzLocalSource = pArgs->wzLocalSource; | ||
| 352 | LPCWSTR wzDownloadSource = pArgs->wzDownloadSource; | ||
| 353 | DWORD64 qwSize = pArgs->qwSize; | ||
| 354 | BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = pArgs->hashType; | ||
| 355 | BYTE* rgbHash = pArgs->rgbHash; | ||
| 356 | DWORD cbHash = pArgs->cbHash; | ||
| 357 | |||
| 358 | ::EnterCriticalSection(&pContext->pEngineState->csActive); | ||
| 359 | |||
| 360 | if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource)) | ||
| 361 | { | ||
| 362 | UpdateUninitialize(&pContext->pEngineState->update); | ||
| 363 | } | ||
| 364 | else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash)) | ||
| 365 | { | ||
| 366 | hr = E_INVALIDARG; | ||
| 367 | } | ||
| 368 | else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash)) | ||
| 369 | { | ||
| 370 | hr = E_INVALIDARG; | ||
| 371 | } | ||
| 372 | else | ||
| 373 | { | ||
| 374 | UpdateUninitialize(&pContext->pEngineState->update); | ||
| 375 | |||
| 376 | if (!wzLocalSource || !*wzLocalSource) | ||
| 377 | { | ||
| 378 | hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pContext->pEngineState->registration.sczExecutableName); | ||
| 379 | ExitOnFailure(hr, "Failed to default local update source"); | ||
| 380 | } | ||
| 381 | |||
| 382 | hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pContext->pEngineState->command.display, pContext->pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pContext->pEngineState->registration.sczActiveParent, pContext->pEngineState->registration.sczAncestors, NULL, pContext->pEngineState->command.wzCommandLine); | ||
| 383 | ExitOnFailure(hr, "Failed to recreate command-line for update bundle."); | ||
| 384 | |||
| 385 | // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached | ||
| 386 | // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the | ||
| 387 | // feed would provide the ID of the update. | ||
| 388 | if (!pContext->pEngineState->registration.fPerMachine) | ||
| 389 | { | ||
| 390 | rs = ::UuidCreate(&guid); | ||
| 391 | hr = HRESULT_FROM_RPC(rs); | ||
| 392 | ExitOnFailure(hr, "Failed to create bundle update guid."); | ||
| 393 | |||
| 394 | if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) | ||
| 395 | { | ||
| 396 | hr = E_OUTOFMEMORY; | ||
| 397 | ExitOnRootFailure(hr, "Failed to convert bundle update guid into string."); | ||
| 398 | } | ||
| 399 | |||
| 400 | sczId = wzGuid; | ||
| 401 | } | ||
| 402 | else | ||
| 403 | { | ||
| 404 | sczId = pContext->pEngineState->registration.sczId; | ||
| 405 | } | ||
| 406 | |||
| 407 | hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, 0), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash); | ||
| 408 | ExitOnFailure(hr, "Failed to set update bundle."); | ||
| 409 | |||
| 410 | pContext->pEngineState->update.fUpdateAvailable = TRUE; | ||
| 411 | } | ||
| 412 | |||
| 413 | LExit: | ||
| 414 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
| 415 | |||
| 416 | ReleaseStr(sczCommandline); | ||
| 417 | ReleaseStr(sczLocalSource); | ||
| 418 | return hr; | ||
| 419 | } | ||
| 420 | |||
| 421 | static HRESULT BAEngineSetLocalSource( | ||
| 422 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 423 | __in BAENGINE_SETLOCALSOURCE_ARGS* pArgs, | ||
| 424 | __in BAENGINE_SETLOCALSOURCE_RESULTS* /*pResults*/ | ||
| 425 | ) | ||
| 426 | { | ||
| 427 | HRESULT hr = S_OK; | ||
| 428 | BURN_CONTAINER* pContainer = NULL; | ||
| 429 | BURN_PAYLOAD* pPayload = NULL; | ||
| 430 | LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; | ||
| 431 | LPCWSTR wzPayloadId = pArgs->wzPayloadId; | ||
| 432 | LPCWSTR wzPath = pArgs->wzPath; | ||
| 433 | |||
| 434 | ::EnterCriticalSection(&pContext->pEngineState->csActive); | ||
| 435 | hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); | ||
| 436 | ExitOnFailure(hr, "Engine is active, cannot change engine state."); | ||
| 437 | |||
| 438 | if (!wzPath || !*wzPath) | ||
| 439 | { | ||
| 440 | hr = E_INVALIDARG; | ||
| 441 | } | ||
| 442 | else if (wzPayloadId && * wzPayloadId) | ||
| 443 | { | ||
| 444 | hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); | ||
| 445 | ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); | ||
| 446 | |||
| 447 | if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) | ||
| 448 | { | ||
| 449 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); | ||
| 450 | ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId); | ||
| 451 | } | ||
| 452 | |||
| 453 | hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0); | ||
| 454 | ExitOnFailure(hr, "Failed to set source path for payload."); | ||
| 455 | } | ||
| 456 | else if (wzPackageOrContainerId && *wzPackageOrContainerId) | ||
| 457 | { | ||
| 458 | hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); | ||
| 459 | ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); | ||
| 460 | |||
| 461 | hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0); | ||
| 462 | ExitOnFailure(hr, "Failed to set source path for container."); | ||
| 463 | } | ||
| 464 | else | ||
| 465 | { | ||
| 466 | hr = E_INVALIDARG; | ||
| 467 | } | ||
| 468 | |||
| 469 | LExit: | ||
| 470 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
| 471 | return hr; | ||
| 472 | } | ||
| 473 | |||
| 474 | static HRESULT BAEngineSetDownloadSource( | ||
| 475 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 476 | __in BAENGINE_SETDOWNLOADSOURCE_ARGS* pArgs, | ||
| 477 | __in BAENGINE_SETDOWNLOADSOURCE_RESULTS* /*pResults*/ | ||
| 478 | ) | ||
| 479 | { | ||
| 480 | HRESULT hr = S_OK; | ||
| 481 | BURN_CONTAINER* pContainer = NULL; | ||
| 482 | BURN_PAYLOAD* pPayload = NULL; | ||
| 483 | DOWNLOAD_SOURCE* pDownloadSource = NULL; | ||
| 484 | LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId; | ||
| 485 | LPCWSTR wzPayloadId = pArgs->wzPayloadId; | ||
| 486 | LPCWSTR wzUrl = pArgs->wzUrl; | ||
| 487 | LPCWSTR wzUser = pArgs->wzUser; | ||
| 488 | LPCWSTR wzPassword = pArgs->wzPassword; | ||
| 489 | |||
| 490 | ::EnterCriticalSection(&pContext->pEngineState->csActive); | ||
| 491 | hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); | ||
| 492 | ExitOnFailure(hr, "Engine is active, cannot change engine state."); | ||
| 493 | |||
| 494 | if (wzPayloadId && *wzPayloadId) | ||
| 495 | { | ||
| 496 | hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload); | ||
| 497 | ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId); | ||
| 498 | |||
| 499 | if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) | ||
| 500 | { | ||
| 501 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); | ||
| 502 | ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId); | ||
| 503 | } | ||
| 504 | |||
| 505 | pDownloadSource = &pPayload->downloadSource; | ||
| 506 | } | ||
| 507 | else if (wzPackageOrContainerId && *wzPackageOrContainerId) | ||
| 508 | { | ||
| 509 | hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer); | ||
| 510 | ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId); | ||
| 511 | |||
| 512 | pDownloadSource = &pContainer->downloadSource; | ||
| 513 | } | ||
| 514 | else | ||
| 515 | { | ||
| 516 | hr = E_INVALIDARG; | ||
| 517 | ExitOnFailure(hr, "BA did not provide container or payload id."); | ||
| 518 | } | ||
| 519 | |||
| 520 | if (wzUrl && *wzUrl) | ||
| 521 | { | ||
| 522 | hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0); | ||
| 523 | ExitOnFailure(hr, "Failed to set download URL."); | ||
| 524 | |||
| 525 | if (wzUser && *wzUser) | ||
| 526 | { | ||
| 527 | hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0); | ||
| 528 | ExitOnFailure(hr, "Failed to set download user."); | ||
| 529 | |||
| 530 | if (wzPassword && *wzPassword) | ||
| 531 | { | ||
| 532 | hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0); | ||
| 533 | ExitOnFailure(hr, "Failed to set download password."); | ||
| 534 | } | ||
| 535 | else // no password. | ||
| 536 | { | ||
| 537 | ReleaseNullStr(pDownloadSource->sczPassword); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | else // no user means no password either. | ||
| 541 | { | ||
| 542 | ReleaseNullStr(pDownloadSource->sczUser); | ||
| 543 | ReleaseNullStr(pDownloadSource->sczPassword); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | else // no URL provided means clear out the whole download source. | ||
| 547 | { | ||
| 548 | ReleaseNullStr(pDownloadSource->sczUrl); | ||
| 549 | ReleaseNullStr(pDownloadSource->sczUser); | ||
| 550 | ReleaseNullStr(pDownloadSource->sczPassword); | ||
| 551 | } | ||
| 552 | |||
| 553 | LExit: | ||
| 554 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
| 555 | return hr; | ||
| 556 | } | ||
| 557 | |||
| 558 | static HRESULT BAEngineSetVariableNumeric( | ||
| 559 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 560 | __in const BAENGINE_SETVARIABLENUMERIC_ARGS* pArgs, | ||
| 561 | __in BAENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/ | ||
| 562 | ) | ||
| 563 | { | ||
| 564 | HRESULT hr = S_OK; | ||
| 565 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 566 | LONGLONG llValue = pArgs->llValue; | ||
| 567 | |||
| 568 | if (wzVariable && *wzVariable) | ||
| 569 | { | ||
| 570 | hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE); | ||
| 571 | ExitOnFailure(hr, "Failed to set numeric variable."); | ||
| 572 | } | ||
| 573 | else | ||
| 574 | { | ||
| 575 | hr = E_INVALIDARG; | ||
| 576 | ExitOnFailure(hr, "BA did not provide variable name."); | ||
| 577 | } | ||
| 578 | |||
| 579 | LExit: | ||
| 580 | return hr; | ||
| 581 | } | ||
| 582 | |||
| 583 | static HRESULT BAEngineSetVariableString( | ||
| 584 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 585 | __in const BAENGINE_SETVARIABLESTRING_ARGS* pArgs, | ||
| 586 | __in BAENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/ | ||
| 587 | ) | ||
| 588 | { | ||
| 589 | HRESULT hr = S_OK; | ||
| 590 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 591 | LPCWSTR wzValue = pArgs->wzValue; | ||
| 592 | |||
| 593 | if (wzVariable && *wzVariable) | ||
| 594 | { | ||
| 595 | hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE); | ||
| 596 | ExitOnFailure(hr, "Failed to set numeric variable."); | ||
| 597 | } | ||
| 598 | else | ||
| 599 | { | ||
| 600 | hr = E_INVALIDARG; | ||
| 601 | ExitOnFailure(hr, "BA did not provide variable name."); | ||
| 602 | } | ||
| 603 | |||
| 604 | LExit: | ||
| 605 | return hr; | ||
| 606 | } | ||
| 607 | |||
| 608 | static HRESULT BAEngineSetVariableVersion( | ||
| 609 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 610 | __in const BAENGINE_SETVARIABLEVERSION_ARGS* pArgs, | ||
| 611 | __in BAENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/ | ||
| 612 | ) | ||
| 613 | { | ||
| 614 | HRESULT hr = S_OK; | ||
| 615 | LPCWSTR wzVariable = pArgs->wzVariable; | ||
| 616 | DWORD64 qwValue = pArgs->qwValue; | ||
| 617 | |||
| 618 | if (wzVariable && *wzVariable) | ||
| 619 | { | ||
| 620 | hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE); | ||
| 621 | ExitOnFailure(hr, "Failed to set version variable."); | ||
| 622 | } | ||
| 623 | else | ||
| 624 | { | ||
| 625 | hr = E_INVALIDARG; | ||
| 626 | ExitOnFailure(hr, "BA did not provide variable name."); | ||
| 627 | } | ||
| 628 | |||
| 629 | LExit: | ||
| 630 | return hr; | ||
| 631 | } | ||
| 632 | |||
| 633 | static HRESULT BAEngineCloseSplashScreen( | ||
| 634 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 635 | __in const BAENGINE_CLOSESPLASHSCREEN_ARGS* /*pArgs*/, | ||
| 636 | __in BAENGINE_CLOSESPLASHSCREEN_RESULTS* /*pResults*/ | ||
| 637 | ) | ||
| 638 | { | ||
| 639 | // If the splash screen is still around, close it. | ||
| 640 | if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen)) | ||
| 641 | { | ||
| 642 | ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); | ||
| 643 | } | ||
| 644 | |||
| 645 | return S_OK; | ||
| 646 | } | ||
| 647 | |||
| 648 | static HRESULT BAEngineDetect( | ||
| 649 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 650 | __in BAENGINE_DETECT_ARGS* pArgs, | ||
| 651 | __in BAENGINE_DETECT_RESULTS* /*pResults*/ | ||
| 652 | ) | ||
| 653 | { | ||
| 654 | HRESULT hr = S_OK; | ||
| 655 | |||
| 656 | if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent))) | ||
| 657 | { | ||
| 658 | ExitWithLastError(hr, "Failed to post detect message."); | ||
| 659 | } | ||
| 660 | |||
| 661 | LExit: | ||
| 662 | return hr; | ||
| 663 | } | ||
| 664 | |||
| 665 | static HRESULT BAEnginePlan( | ||
| 666 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 667 | __in const BAENGINE_PLAN_ARGS* pArgs, | ||
| 668 | __in BAENGINE_PLAN_RESULTS* /*pResults*/ | ||
| 669 | ) | ||
| 670 | { | ||
| 671 | HRESULT hr = S_OK; | ||
| 672 | BOOTSTRAPPER_ACTION action = pArgs->action; | ||
| 673 | |||
| 674 | if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action)) | ||
| 675 | { | ||
| 676 | ExitWithLastError(hr, "Failed to post plan message."); | ||
| 677 | } | ||
| 678 | |||
| 679 | LExit: | ||
| 680 | return hr; | ||
| 681 | } | ||
| 682 | |||
| 683 | static HRESULT BAEngineElevate( | ||
| 684 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 685 | __in const BAENGINE_ELEVATE_ARGS* pArgs, | ||
| 686 | __in BAENGINE_ELEVATE_RESULTS* /*pResults*/ | ||
| 687 | ) | ||
| 688 | { | ||
| 689 | HRESULT hr = S_OK; | ||
| 690 | |||
| 691 | if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe) | ||
| 692 | { | ||
| 693 | hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); | ||
| 694 | } | ||
| 695 | else if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent))) | ||
| 696 | { | ||
| 697 | ExitWithLastError(hr, "Failed to post elevate message."); | ||
| 698 | } | ||
| 699 | |||
| 700 | LExit: | ||
| 701 | return hr; | ||
| 702 | } | ||
| 703 | |||
| 704 | static HRESULT BAEngineApply( | ||
| 705 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 706 | __in const BAENGINE_APPLY_ARGS* pArgs, | ||
| 707 | __in BAENGINE_APPLY_RESULTS* /*pResults*/ | ||
| 708 | ) | ||
| 709 | { | ||
| 710 | HRESULT hr = S_OK; | ||
| 711 | |||
| 712 | if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent))) | ||
| 713 | { | ||
| 714 | ExitWithLastError(hr, "Failed to post apply message."); | ||
| 715 | } | ||
| 716 | |||
| 717 | LExit: | ||
| 718 | return hr; | ||
| 719 | } | ||
| 720 | |||
| 721 | static HRESULT BAEngineQuit( | ||
| 722 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 723 | __in const BAENGINE_QUIT_ARGS* pArgs, | ||
| 724 | __in BAENGINE_QUIT_RESULTS* /*pResults*/ | ||
| 725 | ) | ||
| 726 | { | ||
| 727 | HRESULT hr = S_OK; | ||
| 728 | |||
| 729 | if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast<WPARAM>(pArgs->dwExitCode), 0)) | ||
| 730 | { | ||
| 731 | ExitWithLastError(hr, "Failed to post shutdown message."); | ||
| 732 | } | ||
| 733 | |||
| 734 | LExit: | ||
| 735 | return hr; | ||
| 736 | } | ||
| 737 | |||
| 738 | static HRESULT BAEngineLaunchApprovedExe( | ||
| 739 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext, | ||
| 740 | __in const BAENGINE_LAUNCHAPPROVEDEXE_ARGS* pArgs, | ||
| 741 | __in BAENGINE_LAUNCHAPPROVEDEXE_RESULTS* /*pResults*/ | ||
| 742 | ) | ||
| 743 | { | ||
| 744 | HRESULT hr = S_OK; | ||
| 745 | BURN_APPROVED_EXE* pApprovedExe = NULL; | ||
| 746 | BOOL fLeaveCriticalSection = FALSE; | ||
| 747 | BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); | ||
| 748 | HWND hwndParent = pArgs->hwndParent; | ||
| 749 | LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId; | ||
| 750 | LPCWSTR wzArguments = pArgs->wzArguments; | ||
| 751 | DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout; | ||
| 752 | |||
| 753 | ::EnterCriticalSection(&pContext->pEngineState->csActive); | ||
| 754 | fLeaveCriticalSection = TRUE; | ||
| 755 | hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience); | ||
| 756 | ExitOnFailure(hr, "Engine is active, cannot change engine state."); | ||
| 757 | |||
| 758 | if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId) | ||
| 759 | { | ||
| 760 | ExitFunction1(hr = E_INVALIDARG); | ||
| 761 | } | ||
| 762 | |||
| 763 | hr = ApprovedExesFindById(&pContext->pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe); | ||
| 764 | ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId); | ||
| 765 | |||
| 766 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
| 767 | fLeaveCriticalSection = FALSE; | ||
| 768 | |||
| 769 | hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL); | ||
| 770 | ExitOnFailure(hr, "Failed to copy the id."); | ||
| 771 | |||
| 772 | if (wzArguments) | ||
| 773 | { | ||
| 774 | hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL); | ||
| 775 | ExitOnFailure(hr, "Failed to copy the arguments."); | ||
| 776 | } | ||
| 777 | |||
| 778 | pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout; | ||
| 779 | |||
| 780 | pLaunchApprovedExe->hwndParent = hwndParent; | ||
| 781 | |||
| 782 | if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast<LPARAM>(pLaunchApprovedExe))) | ||
| 783 | { | ||
| 784 | ExitWithLastError(hr, "Failed to post launch approved exe message."); | ||
| 785 | } | ||
| 786 | |||
| 787 | LExit: | ||
| 788 | if (fLeaveCriticalSection) | ||
| 789 | { | ||
| 790 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
| 791 | } | ||
| 792 | |||
| 793 | if (FAILED(hr)) | ||
| 794 | { | ||
| 795 | ApprovedExesUninitializeLaunch(pLaunchApprovedExe); | ||
| 796 | } | ||
| 797 | |||
| 798 | return hr; | ||
| 799 | } | ||
| 800 | |||
| 801 | HRESULT WINAPI EngineForApplicationProc( | ||
| 802 | __in BOOTSTRAPPER_ENGINE_MESSAGE message, | ||
| 803 | __in const LPVOID pvArgs, | ||
| 804 | __inout LPVOID pvResults, | ||
| 805 | __in_opt LPVOID pvContext | ||
| 806 | ) | ||
| 807 | { | ||
| 808 | HRESULT hr = S_OK; | ||
| 809 | BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast<BOOTSTRAPPER_ENGINE_CONTEXT*>(pvContext); | ||
| 810 | |||
| 811 | if (!pContext || !pvArgs || !pvResults) | ||
| 812 | { | ||
| 813 | ExitFunction1(hr = E_INVALIDARG); | ||
| 814 | } | ||
| 815 | |||
| 816 | switch (message) | ||
| 817 | { | ||
| 818 | case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT: | ||
| 819 | hr = BAEngineGetPackageCount(pContext, reinterpret_cast<BAENGINE_GETPACKAGECOUNT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETPACKAGECOUNT_RESULTS*>(pvResults)); | ||
| 820 | break; | ||
| 821 | case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC: | ||
| 822 | hr = BAEngineGetVariableNumeric(pContext, reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_RESULTS*>(pvResults)); | ||
| 823 | break; | ||
| 824 | case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING: | ||
| 825 | hr = BAEngineGetVariableString(pContext, reinterpret_cast<BAENGINE_GETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLESTRING_RESULTS*>(pvResults)); | ||
| 826 | break; | ||
| 827 | case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION: | ||
| 828 | hr = BAEngineGetVariableVersion(pContext, reinterpret_cast<BAENGINE_GETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLEVERSION_RESULTS*>(pvResults)); | ||
| 829 | break; | ||
| 830 | case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING: | ||
| 831 | hr = BAEngineFormatString(pContext, reinterpret_cast<BAENGINE_FORMATSTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_FORMATSTRING_RESULTS*>(pvResults)); | ||
| 832 | break; | ||
| 833 | case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING: | ||
| 834 | hr = BAEngineEscapeString(pContext, reinterpret_cast<BAENGINE_ESCAPESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ESCAPESTRING_RESULTS*>(pvResults)); | ||
| 835 | break; | ||
| 836 | case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION: | ||
| 837 | hr = BAEngineEvaluateCondition(pContext, reinterpret_cast<BAENGINE_EVALUATECONDITION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_EVALUATECONDITION_RESULTS*>(pvResults)); | ||
| 838 | break; | ||
| 839 | case BOOTSTRAPPER_ENGINE_MESSAGE_LOG: | ||
| 840 | hr = BAEngineLog(pContext, reinterpret_cast<BAENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LOG_RESULTS*>(pvResults)); | ||
| 841 | break; | ||
| 842 | case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR: | ||
| 843 | hr = BAEngineSendEmbeddedError(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_RESULTS*>(pvResults)); | ||
| 844 | break; | ||
| 845 | case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS: | ||
| 846 | hr = BAEngineSendEmbeddedProgress(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS*>(pvResults)); | ||
| 847 | break; | ||
| 848 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE: | ||
| 849 | hr = BAEngineSetUpdate(pContext, reinterpret_cast<BAENGINE_SETUPDATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETUPDATE_RESULTS*>(pvResults)); | ||
| 850 | break; | ||
| 851 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE: | ||
| 852 | hr = BAEngineSetLocalSource(pContext, reinterpret_cast<BAENGINE_SETLOCALSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETLOCALSOURCE_RESULTS*>(pvResults)); | ||
| 853 | break; | ||
| 854 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE: | ||
| 855 | hr = BAEngineSetDownloadSource(pContext, reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_RESULTS*>(pvResults)); | ||
| 856 | break; | ||
| 857 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC: | ||
| 858 | hr = BAEngineSetVariableNumeric(pContext, reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults)); | ||
| 859 | break; | ||
| 860 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING: | ||
| 861 | hr = BAEngineSetVariableString(pContext, reinterpret_cast<BAENGINE_SETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLESTRING_RESULTS*>(pvResults)); | ||
| 862 | break; | ||
| 863 | case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION: | ||
| 864 | hr = BAEngineSetVariableVersion(pContext, reinterpret_cast<BAENGINE_SETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLEVERSION_RESULTS*>(pvResults)); | ||
| 865 | break; | ||
| 866 | case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN: | ||
| 867 | hr = BAEngineCloseSplashScreen(pContext, reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_RESULTS*>(pvResults)); | ||
| 868 | break; | ||
| 869 | case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT: | ||
| 870 | hr = BAEngineDetect(pContext, reinterpret_cast<BAENGINE_DETECT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_DETECT_RESULTS*>(pvResults)); | ||
| 871 | break; | ||
| 872 | case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN: | ||
| 873 | hr = BAEnginePlan(pContext, reinterpret_cast<BAENGINE_PLAN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_PLAN_RESULTS*>(pvResults)); | ||
| 874 | break; | ||
| 875 | case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE: | ||
| 876 | hr = BAEngineElevate(pContext, reinterpret_cast<BAENGINE_ELEVATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ELEVATE_RESULTS*>(pvResults)); | ||
| 877 | break; | ||
| 878 | case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY: | ||
| 879 | hr = BAEngineApply(pContext, reinterpret_cast<BAENGINE_APPLY_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_APPLY_RESULTS*>(pvResults)); | ||
| 880 | break; | ||
| 881 | case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT: | ||
| 882 | hr = BAEngineQuit(pContext, reinterpret_cast<BAENGINE_QUIT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_QUIT_RESULTS*>(pvResults)); | ||
| 883 | break; | ||
| 884 | case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE: | ||
| 885 | hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_RESULTS*>(pvResults)); | ||
| 886 | break; | ||
| 887 | default: | ||
| 888 | hr = E_NOTIMPL; | ||
| 889 | break; | ||
| 890 | } | ||
| 891 | |||
| 892 | LExit: | ||
| 893 | return hr; | ||
| 894 | } | ||
diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h new file mode 100644 index 00000000..1b755acc --- /dev/null +++ b/src/engine/EngineForApplication.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | // constants | ||
| 10 | |||
| 11 | enum WM_BURN | ||
| 12 | { | ||
| 13 | WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. | ||
| 14 | |||
| 15 | WM_BURN_DETECT, | ||
| 16 | WM_BURN_PLAN, | ||
| 17 | WM_BURN_ELEVATE, | ||
| 18 | WM_BURN_APPLY, | ||
| 19 | WM_BURN_LAUNCH_APPROVED_EXE, | ||
| 20 | WM_BURN_QUIT, | ||
| 21 | |||
| 22 | WM_BURN_LAST, // this enum value must always be last. | ||
| 23 | }; | ||
| 24 | |||
| 25 | // structs | ||
| 26 | |||
| 27 | struct BOOTSTRAPPER_ENGINE_CONTEXT | ||
| 28 | { | ||
| 29 | BURN_ENGINE_STATE* pEngineState; | ||
| 30 | DWORD dwThreadId; | ||
| 31 | }; | ||
| 32 | |||
| 33 | // function declarations | ||
| 34 | |||
| 35 | HRESULT WINAPI EngineForApplicationProc( | ||
| 36 | __in BOOTSTRAPPER_ENGINE_MESSAGE message, | ||
| 37 | __in const LPVOID pvArgs, | ||
| 38 | __inout LPVOID pvResults, | ||
| 39 | __in_opt LPVOID pvContext | ||
| 40 | ); | ||
| 41 | |||
| 42 | #if defined(__cplusplus) | ||
| 43 | } | ||
| 44 | #endif | ||
diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp new file mode 100644 index 00000000..0cef9ac8 --- /dev/null +++ b/src/engine/apply.cpp | |||
| @@ -0,0 +1,2516 @@ | |||
| 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 | |||
| 5 | |||
| 6 | const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; | ||
| 7 | |||
| 8 | // structs | ||
| 9 | |||
| 10 | struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT | ||
| 11 | { | ||
| 12 | BURN_USER_EXPERIENCE* pUX; | ||
| 13 | BURN_CONTAINER* pContainer; | ||
| 14 | BURN_PACKAGE* pPackage; | ||
| 15 | BURN_PAYLOAD* pPayload; | ||
| 16 | DWORD64 qwCacheProgress; | ||
| 17 | DWORD64 qwTotalCacheSize; | ||
| 18 | |||
| 19 | BOOL fCancel; | ||
| 20 | BOOL fError; | ||
| 21 | }; | ||
| 22 | |||
| 23 | typedef struct _BURN_EXECUTE_CONTEXT | ||
| 24 | { | ||
| 25 | BURN_USER_EXPERIENCE* pUX; | ||
| 26 | BOOL fRollback; | ||
| 27 | BURN_PACKAGE* pExecutingPackage; | ||
| 28 | DWORD cExecutedPackages; | ||
| 29 | DWORD cExecutePackagesTotal; | ||
| 30 | DWORD* pcOverallProgressTicks; | ||
| 31 | } BURN_EXECUTE_CONTEXT; | ||
| 32 | |||
| 33 | |||
| 34 | // internal function declarations | ||
| 35 | static HRESULT WINAPI AuthenticationRequired( | ||
| 36 | __in LPVOID pData, | ||
| 37 | __in HINTERNET hUrl, | ||
| 38 | __in long lHttpCode, | ||
| 39 | __out BOOL* pfRetrySend, | ||
| 40 | __out BOOL* pfRetry | ||
| 41 | ); | ||
| 42 | |||
| 43 | static HRESULT ExecuteDependentRegistrationActions( | ||
| 44 | __in HANDLE hPipe, | ||
| 45 | __in const BURN_REGISTRATION* pRegistration, | ||
| 46 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
| 47 | __in DWORD cActions | ||
| 48 | ); | ||
| 49 | static HRESULT ExtractContainer( | ||
| 50 | __in HANDLE hSourceEngineFile, | ||
| 51 | __in BURN_CONTAINER* pContainer, | ||
| 52 | __in_z LPCWSTR wzContainerPath, | ||
| 53 | __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, | ||
| 54 | __in DWORD cExtractPayloads | ||
| 55 | ); | ||
| 56 | static void UpdateCacheSuccessProgress( | ||
| 57 | __in BURN_PLAN* pPlan, | ||
| 58 | __in BURN_CACHE_ACTION* pCacheAction, | ||
| 59 | __inout DWORD64* pqwSuccessfulCachedProgress | ||
| 60 | ); | ||
| 61 | static HRESULT LayoutBundle( | ||
| 62 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 63 | __in BURN_VARIABLES* pVariables, | ||
| 64 | __in HANDLE hPipe, | ||
| 65 | __in_z LPCWSTR wzExecutableName, | ||
| 66 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 67 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 68 | __in DWORD64 qwSuccessfulCacheProgress, | ||
| 69 | __in DWORD64 qwTotalCacheSize | ||
| 70 | ); | ||
| 71 | static HRESULT AcquireContainerOrPayload( | ||
| 72 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 73 | __in BURN_VARIABLES* pVariables, | ||
| 74 | __in_opt BURN_CONTAINER* pContainer, | ||
| 75 | __in_opt BURN_PACKAGE* pPackage, | ||
| 76 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 77 | __in LPCWSTR wzDestinationPath, | ||
| 78 | __in DWORD64 qwSuccessfulCacheProgress, | ||
| 79 | __in DWORD64 qwTotalCacheSize | ||
| 80 | ); | ||
| 81 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
| 82 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 83 | __in HANDLE hPipe, | ||
| 84 | __in_opt BURN_CONTAINER* pContainer, | ||
| 85 | __in_opt BURN_PACKAGE* pPackage, | ||
| 86 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 87 | __in BOOL fAlreadyProvidedProgress, | ||
| 88 | __in DWORD64 qwSuccessfullyCacheProgress, | ||
| 89 | __in DWORD64 qwTotalCacheSize, | ||
| 90 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 91 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 92 | __in BOOL fMove, | ||
| 93 | __in DWORD cTryAgainAttempts, | ||
| 94 | __out BOOL* pfRetry | ||
| 95 | ); | ||
| 96 | static HRESULT PromptForSource( | ||
| 97 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 98 | __in_z LPCWSTR wzPackageOrContainerId, | ||
| 99 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 100 | __in_z LPCWSTR wzLocalSource, | ||
| 101 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 102 | __out BOOL* pfRetry, | ||
| 103 | __out BOOL* pfDownload | ||
| 104 | ); | ||
| 105 | static HRESULT CopyPayload( | ||
| 106 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
| 107 | __in_z LPCWSTR wzSourcePath, | ||
| 108 | __in_z LPCWSTR wzDestinationPath | ||
| 109 | ); | ||
| 110 | static HRESULT DownloadPayload( | ||
| 111 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
| 112 | __in_z LPCWSTR wzDestinationPath | ||
| 113 | ); | ||
| 114 | static DWORD CALLBACK CacheProgressRoutine( | ||
| 115 | __in LARGE_INTEGER TotalFileSize, | ||
| 116 | __in LARGE_INTEGER TotalBytesTransferred, | ||
| 117 | __in LARGE_INTEGER StreamSize, | ||
| 118 | __in LARGE_INTEGER StreamBytesTransferred, | ||
| 119 | __in DWORD dwStreamNumber, | ||
| 120 | __in DWORD dwCallbackReason, | ||
| 121 | __in HANDLE hSourceFile, | ||
| 122 | __in HANDLE hDestinationFile, | ||
| 123 | __in_opt LPVOID lpData | ||
| 124 | ); | ||
| 125 | static void DoRollbackCache( | ||
| 126 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 127 | __in BURN_PLAN* pPlan, | ||
| 128 | __in HANDLE hPipe, | ||
| 129 | __in DWORD dwCheckpoint | ||
| 130 | ); | ||
| 131 | static HRESULT DoExecuteAction( | ||
| 132 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 133 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 134 | __in_opt HANDLE hCacheThread, | ||
| 135 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 136 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
| 137 | __out DWORD* pdwCheckpoint, | ||
| 138 | __out BOOL* pfKeepRegistration, | ||
| 139 | __out BOOL* pfSuspend, | ||
| 140 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 141 | ); | ||
| 142 | static HRESULT DoRollbackActions( | ||
| 143 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 144 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 145 | __in DWORD dwCheckpoint, | ||
| 146 | __in BOOL fInTransaction, | ||
| 147 | __out BOOL* pfKeepRegistration, | ||
| 148 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 149 | ); | ||
| 150 | static HRESULT ExecuteExePackage( | ||
| 151 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 152 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 153 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 154 | __in BOOL fRollback, | ||
| 155 | __out BOOL* pfRetry, | ||
| 156 | __out BOOL* pfSuspend, | ||
| 157 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 158 | ); | ||
| 159 | static HRESULT ExecuteMsiPackage( | ||
| 160 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 161 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 162 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 163 | __in BOOL fRollback, | ||
| 164 | __out BOOL* pfRetry, | ||
| 165 | __out BOOL* pfSuspend, | ||
| 166 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 167 | ); | ||
| 168 | static HRESULT ExecuteMspPackage( | ||
| 169 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 170 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 171 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 172 | __in BOOL fRollback, | ||
| 173 | __out BOOL* pfRetry, | ||
| 174 | __out BOOL* pfSuspend, | ||
| 175 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 176 | ); | ||
| 177 | static HRESULT ExecuteMsuPackage( | ||
| 178 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 179 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 180 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 181 | __in BOOL fRollback, | ||
| 182 | __in BOOL fStopWusaService, | ||
| 183 | __out BOOL* pfRetry, | ||
| 184 | __out BOOL* pfSuspend, | ||
| 185 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 186 | ); | ||
| 187 | static HRESULT ExecutePackageProviderAction( | ||
| 188 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 189 | __in BURN_EXECUTE_ACTION* pAction, | ||
| 190 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 191 | ); | ||
| 192 | static HRESULT ExecuteDependencyAction( | ||
| 193 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 194 | __in BURN_EXECUTE_ACTION* pAction, | ||
| 195 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 196 | ); | ||
| 197 | static HRESULT ExecuteCompatiblePackageAction( | ||
| 198 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 199 | __in BURN_EXECUTE_ACTION* pAction | ||
| 200 | ); | ||
| 201 | static HRESULT CleanPackage( | ||
| 202 | __in HANDLE hElevatedPipe, | ||
| 203 | __in BURN_PACKAGE* pPackage | ||
| 204 | ); | ||
| 205 | static int GenericExecuteMessageHandler( | ||
| 206 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
| 207 | __in LPVOID pvContext | ||
| 208 | ); | ||
| 209 | static int MsiExecuteMessageHandler( | ||
| 210 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
| 211 | __in_opt LPVOID pvContext | ||
| 212 | ); | ||
| 213 | static HRESULT ReportOverallProgressTicks( | ||
| 214 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 215 | __in BOOL fRollback, | ||
| 216 | __in DWORD cOverallProgressTicksTotal, | ||
| 217 | __in DWORD cOverallProgressTicks | ||
| 218 | ); | ||
| 219 | static HRESULT ExecutePackageComplete( | ||
| 220 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 221 | __in BURN_VARIABLES* pVariables, | ||
| 222 | __in BURN_PACKAGE* pPackage, | ||
| 223 | __in HRESULT hrOverall, | ||
| 224 | __in HRESULT hrExecute, | ||
| 225 | __in BOOL fRollback, | ||
| 226 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
| 227 | __out BOOL* pfRetry, | ||
| 228 | __out BOOL* pfSuspend | ||
| 229 | ); | ||
| 230 | |||
| 231 | static HRESULT DoMsiBeginTransaction( | ||
| 232 | __in BURN_EXECUTE_CONTEXT *context | ||
| 233 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 234 | ); | ||
| 235 | static HRESULT DoMsiCommitTransaction( | ||
| 236 | __in BURN_EXECUTE_CONTEXT *context | ||
| 237 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 238 | ); | ||
| 239 | static HRESULT DoMsiRollbackTransaction( | ||
| 240 | __in BURN_EXECUTE_CONTEXT *context | ||
| 241 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 242 | ); | ||
| 243 | static HRESULT ExecuteMsiBeginTransaction( | ||
| 244 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 245 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 246 | ); | ||
| 247 | static HRESULT ExecuteMsiCommitTransaction( | ||
| 248 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 249 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 250 | ); | ||
| 251 | static HRESULT ExecuteMsiRollbackTransaction( | ||
| 252 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 253 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 254 | ); | ||
| 255 | |||
| 256 | // function definitions | ||
| 257 | |||
| 258 | extern "C" void ApplyInitialize() | ||
| 259 | { | ||
| 260 | // Prevent the system from sleeping. | ||
| 261 | ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); | ||
| 262 | } | ||
| 263 | |||
| 264 | extern "C" void ApplyUninitialize() | ||
| 265 | { | ||
| 266 | ::SetThreadExecutionState(ES_CONTINUOUS); | ||
| 267 | } | ||
| 268 | |||
| 269 | extern "C" HRESULT ApplySetVariables( | ||
| 270 | __in BURN_VARIABLES* pVariables | ||
| 271 | ) | ||
| 272 | { | ||
| 273 | HRESULT hr = S_OK; | ||
| 274 | |||
| 275 | hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE); | ||
| 276 | ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable."); | ||
| 277 | |||
| 278 | LExit: | ||
| 279 | return hr; | ||
| 280 | } | ||
| 281 | |||
| 282 | extern "C" void ApplyReset( | ||
| 283 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 284 | __in BURN_PACKAGES* pPackages | ||
| 285 | ) | ||
| 286 | { | ||
| 287 | UserExperienceExecuteReset(pUX); | ||
| 288 | |||
| 289 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 290 | { | ||
| 291 | BURN_PACKAGE* pPackage = pPackages->rgPackages + i; | ||
| 292 | pPackage->hrCacheResult = S_OK; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | extern "C" HRESULT ApplyLock( | ||
| 297 | __in BOOL /*fPerMachine*/, | ||
| 298 | __out HANDLE* /*phLock*/ | ||
| 299 | ) | ||
| 300 | { | ||
| 301 | HRESULT hr = S_OK; | ||
| 302 | #if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed. | ||
| 303 | DWORD er = ERROR_SUCCESS; | ||
| 304 | HANDLE hLock = NULL; | ||
| 305 | |||
| 306 | hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock"); | ||
| 307 | ExitOnNullWithLastError(hLock, hr, "Failed to create lock."); | ||
| 308 | |||
| 309 | er = ::GetLastError(); | ||
| 310 | if (ERROR_ALREADY_EXISTS == er) | ||
| 311 | { | ||
| 312 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING)); | ||
| 313 | } | ||
| 314 | |||
| 315 | *phLock = hLock; | ||
| 316 | hLock = NULL; | ||
| 317 | |||
| 318 | LExit: | ||
| 319 | ReleaseHandle(hLock); | ||
| 320 | #endif | ||
| 321 | return hr; | ||
| 322 | } | ||
| 323 | |||
| 324 | extern "C" HRESULT ApplyRegister( | ||
| 325 | __in BURN_ENGINE_STATE* pEngineState | ||
| 326 | ) | ||
| 327 | { | ||
| 328 | HRESULT hr = S_OK; | ||
| 329 | LPWSTR sczEngineWorkingPath = NULL; | ||
| 330 | |||
| 331 | hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience); | ||
| 332 | ExitOnRootFailure(hr, "BA aborted register begin."); | ||
| 333 | |||
| 334 | // If we have a resume mode that suggests the bundle is on the machine. | ||
| 335 | if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType) | ||
| 336 | { | ||
| 337 | // resume previous session | ||
| 338 | if (pEngineState->registration.fPerMachine) | ||
| 339 | { | ||
| 340 | hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables); | ||
| 341 | ExitOnFailure(hr, "Failed to resume registration session in per-machine process."); | ||
| 342 | } | ||
| 343 | else | ||
| 344 | { | ||
| 345 | hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables); | ||
| 346 | ExitOnFailure(hr, "Failed to resume registration session."); | ||
| 347 | } | ||
| 348 | } | ||
| 349 | else // need to complete registration on the machine. | ||
| 350 | { | ||
| 351 | hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath); | ||
| 352 | ExitOnFailure(hr, "Failed to calculate working path for engine."); | ||
| 353 | |||
| 354 | // begin new session | ||
| 355 | if (pEngineState->registration.fPerMachine) | ||
| 356 | { | ||
| 357 | hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
| 358 | ExitOnFailure(hr, "Failed to begin registration session in per-machine process."); | ||
| 359 | } | ||
| 360 | else | ||
| 361 | { | ||
| 362 | hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize); | ||
| 363 | ExitOnFailure(hr, "Failed to begin registration session."); | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | // Apply any registration actions. | ||
| 368 | HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions); | ||
| 369 | UNREFERENCED_PARAMETER(hrExecuteRegistration); | ||
| 370 | |||
| 371 | // Try to save engine state. | ||
| 372 | hr = CoreSaveEngineState(pEngineState); | ||
| 373 | if (FAILED(hr)) | ||
| 374 | { | ||
| 375 | LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); | ||
| 376 | hr = S_OK; | ||
| 377 | } | ||
| 378 | |||
| 379 | LExit: | ||
| 380 | UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); | ||
| 381 | ReleaseStr(sczEngineWorkingPath); | ||
| 382 | |||
| 383 | return hr; | ||
| 384 | } | ||
| 385 | |||
| 386 | extern "C" HRESULT ApplyUnregister( | ||
| 387 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 388 | __in BOOL fFailedOrRollback, | ||
| 389 | __in BOOL fKeepRegistration, | ||
| 390 | __in BOOL fSuspend, | ||
| 391 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
| 392 | ) | ||
| 393 | { | ||
| 394 | HRESULT hr = S_OK; | ||
| 395 | BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE; | ||
| 396 | |||
| 397 | hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience); | ||
| 398 | ExitOnRootFailure(hr, "BA aborted unregister begin."); | ||
| 399 | |||
| 400 | // Calculate the correct resume mode. If a restart has been initiated, that trumps all other | ||
| 401 | // modes. If the user chose to suspend the install then we'll use that as the resume mode. | ||
| 402 | // Barring those special cases, if it was determined that we should keep the registration then | ||
| 403 | // do that, otherwise the resume mode was initialized to none and registration will be removed. | ||
| 404 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
| 405 | { | ||
| 406 | resumeMode = BURN_RESUME_MODE_REBOOT_PENDING; | ||
| 407 | } | ||
| 408 | else if (fSuspend) | ||
| 409 | { | ||
| 410 | resumeMode = BURN_RESUME_MODE_SUSPEND; | ||
| 411 | } | ||
| 412 | else if (fKeepRegistration) | ||
| 413 | { | ||
| 414 | resumeMode = BURN_RESUME_MODE_ARP; | ||
| 415 | } | ||
| 416 | |||
| 417 | // If apply failed in any way and we're going to be keeping the bundle registered then | ||
| 418 | // execute any rollback dependency registration actions. | ||
| 419 | if (fFailedOrRollback && fKeepRegistration) | ||
| 420 | { | ||
| 421 | // Execute any rollback registration actions. | ||
| 422 | HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions); | ||
| 423 | UNREFERENCED_PARAMETER(hrRegistrationRollback); | ||
| 424 | } | ||
| 425 | |||
| 426 | if (pEngineState->registration.fPerMachine) | ||
| 427 | { | ||
| 428 | hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
| 429 | ExitOnFailure(hr, "Failed to end session in per-machine process."); | ||
| 430 | } | ||
| 431 | else | ||
| 432 | { | ||
| 433 | hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction); | ||
| 434 | ExitOnFailure(hr, "Failed to end session in per-user process."); | ||
| 435 | } | ||
| 436 | |||
| 437 | pEngineState->resumeMode = resumeMode; | ||
| 438 | |||
| 439 | LExit: | ||
| 440 | UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); | ||
| 441 | |||
| 442 | return hr; | ||
| 443 | } | ||
| 444 | |||
| 445 | extern "C" HRESULT ApplyCache( | ||
| 446 | __in HANDLE hSourceEngineFile, | ||
| 447 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 448 | __in BURN_VARIABLES* pVariables, | ||
| 449 | __in BURN_PLAN* pPlan, | ||
| 450 | __in HANDLE hPipe, | ||
| 451 | __inout DWORD* pcOverallProgressTicks, | ||
| 452 | __out BOOL* pfRollback | ||
| 453 | ) | ||
| 454 | { | ||
| 455 | HRESULT hr = S_OK; | ||
| 456 | DWORD dwCheckpoint = 0; | ||
| 457 | BOOL fRetry = FALSE; | ||
| 458 | DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 459 | BURN_PACKAGE* pStartedPackage = NULL; | ||
| 460 | DWORD64 qwSuccessfulCachedProgress = 0; | ||
| 461 | |||
| 462 | // Allow us to retry and skip packages. | ||
| 463 | DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 464 | DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 465 | |||
| 466 | hr = UserExperienceOnCacheBegin(pUX); | ||
| 467 | ExitOnRootFailure(hr, "BA aborted cache."); | ||
| 468 | |||
| 469 | do | ||
| 470 | { | ||
| 471 | hr = S_OK; | ||
| 472 | fRetry = FALSE; | ||
| 473 | |||
| 474 | // Allow us to retry just a container or payload. | ||
| 475 | LPCWSTR wzRetryId = NULL; | ||
| 476 | DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 477 | BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; | ||
| 478 | |||
| 479 | // cache actions | ||
| 480 | for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i) | ||
| 481 | { | ||
| 482 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; | ||
| 483 | BOOL fRetryContainerOrPayload = FALSE; | ||
| 484 | cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE; | ||
| 485 | |||
| 486 | if (pCacheAction->fSkipUntilRetried) | ||
| 487 | { | ||
| 488 | // If this action was retried, let's make sure it will not be skipped any longer. | ||
| 489 | if (iRetryAction == i) | ||
| 490 | { | ||
| 491 | pCacheAction->fSkipUntilRetried = FALSE; | ||
| 492 | } | ||
| 493 | else // skip the action. | ||
| 494 | { | ||
| 495 | continue; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | switch (pCacheAction->type) | ||
| 500 | { | ||
| 501 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
| 502 | dwCheckpoint = pCacheAction->checkpoint.dwId; | ||
| 503 | break; | ||
| 504 | |||
| 505 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
| 506 | hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
| 507 | if (SUCCEEDED(hr)) | ||
| 508 | { | ||
| 509 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 510 | ++(*pcOverallProgressTicks); | ||
| 511 | |||
| 512 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks); | ||
| 513 | if (FAILED(hr)) | ||
| 514 | { | ||
| 515 | LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | break; | ||
| 519 | |||
| 520 | case BURN_CACHE_ACTION_TYPE_PACKAGE_START: | ||
| 521 | iPackageStartAction = i; // if we retry this package, we'll start here in the plan. | ||
| 522 | iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan. | ||
| 523 | pStartedPackage = pCacheAction->packageStart.pPackage; | ||
| 524 | |||
| 525 | hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal); | ||
| 526 | if (FAILED(hr)) | ||
| 527 | { | ||
| 528 | LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL); | ||
| 529 | } | ||
| 530 | break; | ||
| 531 | |||
| 532 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
| 533 | hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
| 534 | if (SUCCEEDED(hr)) | ||
| 535 | { | ||
| 536 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 537 | } | ||
| 538 | else | ||
| 539 | { | ||
| 540 | LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL); | ||
| 541 | } | ||
| 542 | break; | ||
| 543 | |||
| 544 | case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: | ||
| 545 | // If this action is to be skipped until the acquire action is not skipped and the other | ||
| 546 | // action is still being skipped then skip this action. | ||
| 547 | if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried) | ||
| 548 | { | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | |||
| 552 | hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads); | ||
| 553 | if (FAILED(hr)) | ||
| 554 | { | ||
| 555 | LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL); | ||
| 556 | } | ||
| 557 | break; | ||
| 558 | |||
| 559 | case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: | ||
| 560 | hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
| 561 | if (SUCCEEDED(hr)) | ||
| 562 | { | ||
| 563 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 564 | } | ||
| 565 | else | ||
| 566 | { | ||
| 567 | LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath); | ||
| 568 | |||
| 569 | if (fRetryContainerOrPayload) | ||
| 570 | { | ||
| 571 | wzRetryId = pCacheAction->layoutContainer.pContainer->sczId; | ||
| 572 | iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction; | ||
| 573 | |||
| 574 | ++pCacheAction->layoutContainer.cTryAgainAttempts; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | break; | ||
| 578 | |||
| 579 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
| 580 | hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal); | ||
| 581 | if (SUCCEEDED(hr)) | ||
| 582 | { | ||
| 583 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 584 | } | ||
| 585 | else | ||
| 586 | { | ||
| 587 | LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL); | ||
| 588 | } | ||
| 589 | break; | ||
| 590 | |||
| 591 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
| 592 | hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
| 593 | if (SUCCEEDED(hr)) | ||
| 594 | { | ||
| 595 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 596 | } | ||
| 597 | else | ||
| 598 | { | ||
| 599 | LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL); | ||
| 600 | |||
| 601 | if (fRetryContainerOrPayload) | ||
| 602 | { | ||
| 603 | wzRetryId = pCacheAction->cachePayload.pPayload->sczKey; | ||
| 604 | iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction; | ||
| 605 | |||
| 606 | ++pCacheAction->cachePayload.cTryAgainAttempts; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | break; | ||
| 610 | |||
| 611 | case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: | ||
| 612 | hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload); | ||
| 613 | if (SUCCEEDED(hr)) | ||
| 614 | { | ||
| 615 | UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress); | ||
| 616 | } | ||
| 617 | else | ||
| 618 | { | ||
| 619 | LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath); | ||
| 620 | |||
| 621 | if (fRetryContainerOrPayload) | ||
| 622 | { | ||
| 623 | wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey; | ||
| 624 | iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction; | ||
| 625 | |||
| 626 | ++pCacheAction->layoutPayload.cTryAgainAttempts; | ||
| 627 | } | ||
| 628 | } | ||
| 629 | break; | ||
| 630 | |||
| 631 | case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: | ||
| 632 | AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed."); | ||
| 633 | |||
| 634 | hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1); | ||
| 635 | if (FAILED(hr)) | ||
| 636 | { | ||
| 637 | LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL); | ||
| 638 | } | ||
| 639 | else | ||
| 640 | { | ||
| 641 | ++(*pcOverallProgressTicks); | ||
| 642 | |||
| 643 | UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); | ||
| 644 | |||
| 645 | pStartedPackage->hrCacheResult = hr; | ||
| 646 | |||
| 647 | iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 648 | iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 649 | pStartedPackage = NULL; | ||
| 650 | } | ||
| 651 | break; | ||
| 652 | |||
| 653 | case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: | ||
| 654 | if (!::SetEvent(pCacheAction->syncpoint.hEvent)) | ||
| 655 | { | ||
| 656 | ExitWithLastError(hr, "Failed to set syncpoint event."); | ||
| 657 | } | ||
| 658 | break; | ||
| 659 | |||
| 660 | default: | ||
| 661 | AssertSz(FALSE, "Unknown cache action."); | ||
| 662 | break; | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction) | ||
| 667 | { | ||
| 668 | Assert(wzRetryId); | ||
| 669 | |||
| 670 | LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL); | ||
| 671 | |||
| 672 | iRetryAction = iRetryContainerOrPayloadAction; | ||
| 673 | fRetry = TRUE; | ||
| 674 | } | ||
| 675 | else if (pStartedPackage) | ||
| 676 | { | ||
| 677 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction); | ||
| 678 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction); | ||
| 679 | |||
| 680 | cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE; | ||
| 681 | UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction); | ||
| 682 | if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction) | ||
| 683 | { | ||
| 684 | LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL); | ||
| 685 | |||
| 686 | iRetryAction = iPackageStartAction; | ||
| 687 | fRetry = TRUE; | ||
| 688 | } | ||
| 689 | else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures. | ||
| 690 | { | ||
| 691 | LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr); | ||
| 692 | |||
| 693 | ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package. | ||
| 694 | |||
| 695 | iRetryAction = iPackageCompleteAction + 1; | ||
| 696 | fRetry = TRUE; | ||
| 697 | } | ||
| 698 | |||
| 699 | pStartedPackage->hrCacheResult = hr; | ||
| 700 | |||
| 701 | iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 702 | iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 703 | pStartedPackage = NULL; | ||
| 704 | } | ||
| 705 | else | ||
| 706 | { | ||
| 707 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); | ||
| 708 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); | ||
| 709 | } | ||
| 710 | } while (fRetry); | ||
| 711 | |||
| 712 | LExit: | ||
| 713 | Assert(NULL == pStartedPackage); | ||
| 714 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction); | ||
| 715 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction); | ||
| 716 | |||
| 717 | if (FAILED(hr)) | ||
| 718 | { | ||
| 719 | DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint); | ||
| 720 | *pfRollback = TRUE; | ||
| 721 | } | ||
| 722 | |||
| 723 | // Clean up any remanents in the cache. | ||
| 724 | if (INVALID_HANDLE_VALUE != hPipe) | ||
| 725 | { | ||
| 726 | ElevationCacheCleanup(hPipe); | ||
| 727 | } | ||
| 728 | |||
| 729 | CacheCleanup(FALSE, pPlan->wzBundleId); | ||
| 730 | |||
| 731 | UserExperienceOnCacheComplete(pUX, hr); | ||
| 732 | return hr; | ||
| 733 | } | ||
| 734 | |||
| 735 | extern "C" HRESULT ApplyExecute( | ||
| 736 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 737 | __in_opt HANDLE hCacheThread, | ||
| 738 | __inout DWORD* pcOverallProgressTicks, | ||
| 739 | __out BOOL* pfKeepRegistration, | ||
| 740 | __out BOOL* pfRollback, | ||
| 741 | __out BOOL* pfSuspend, | ||
| 742 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 743 | ) | ||
| 744 | { | ||
| 745 | HRESULT hr = S_OK; | ||
| 746 | DWORD dwCheckpoint = 0; | ||
| 747 | BURN_EXECUTE_CONTEXT context = { }; | ||
| 748 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
| 749 | BOOL fSeekNextRollbackBoundary = FALSE; | ||
| 750 | BOOL fInTransaction = FALSE; | ||
| 751 | |||
| 752 | context.pUX = &pEngineState->userExperience; | ||
| 753 | context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal; | ||
| 754 | context.pcOverallProgressTicks = pcOverallProgressTicks; | ||
| 755 | |||
| 756 | // Send execute begin to BA. | ||
| 757 | hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal); | ||
| 758 | ExitOnRootFailure(hr, "BA aborted execute begin."); | ||
| 759 | |||
| 760 | // Do execute actions. | ||
| 761 | for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i) | ||
| 762 | { | ||
| 763 | BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i]; | ||
| 764 | if (pExecuteAction->fDeleted) | ||
| 765 | { | ||
| 766 | continue; | ||
| 767 | } | ||
| 768 | |||
| 769 | // Transaction end/start | ||
| 770 | if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) | ||
| 771 | { | ||
| 772 | // End previous transaction | ||
| 773 | if (fInTransaction) | ||
| 774 | { | ||
| 775 | LogString(REPORT_STANDARD, "Committing MSI transaction\n"); | ||
| 776 | hr = DoMsiCommitTransaction(&context, pEngineState); | ||
| 777 | ExitOnFailure(hr, "Failed committing an MSI transaction"); | ||
| 778 | fInTransaction = FALSE; | ||
| 779 | } | ||
| 780 | |||
| 781 | // Start New transaction | ||
| 782 | if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction) | ||
| 783 | { | ||
| 784 | // Transactions don't go together with DisableRollback. | ||
| 785 | if (pEngineState->fDisableRollback) | ||
| 786 | { | ||
| 787 | LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n"); | ||
| 788 | } | ||
| 789 | else | ||
| 790 | { | ||
| 791 | LogString(REPORT_STANDARD, "Starting a new MSI transaction\n"); | ||
| 792 | hr = DoMsiBeginTransaction(&context, pEngineState); | ||
| 793 | ExitOnFailure(hr, "Failed beginning an MSI transaction"); | ||
| 794 | fInTransaction = TRUE; | ||
| 795 | } | ||
| 796 | } | ||
| 797 | } | ||
| 798 | |||
| 799 | // If we are seeking the next rollback boundary, skip if this action wasn't it. | ||
| 800 | if (fSeekNextRollbackBoundary) | ||
| 801 | { | ||
| 802 | if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type) | ||
| 803 | { | ||
| 804 | continue; | ||
| 805 | } | ||
| 806 | else | ||
| 807 | { | ||
| 808 | fSeekNextRollbackBoundary = FALSE; | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | // Execute the action. | ||
| 813 | hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart); | ||
| 814 | |||
| 815 | if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
| 816 | { | ||
| 817 | if (fInTransaction) | ||
| 818 | { | ||
| 819 | hr = E_INVALIDSTATE; | ||
| 820 | LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback."); | ||
| 821 | } | ||
| 822 | else | ||
| 823 | { | ||
| 824 | ExitFunction(); | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | if (FAILED(hr)) | ||
| 829 | { | ||
| 830 | // If we failed, but rollback is disabled just bail with our error code. | ||
| 831 | if (pEngineState->fDisableRollback) | ||
| 832 | { | ||
| 833 | *pfRollback = TRUE; | ||
| 834 | break; | ||
| 835 | } | ||
| 836 | else // the action failed, roll back to previous rollback boundary. | ||
| 837 | { | ||
| 838 | HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart); | ||
| 839 | UNREFERENCED_PARAMETER(hrRollback); | ||
| 840 | fInTransaction = FALSE; | ||
| 841 | |||
| 842 | // If the rollback boundary is vital, end execution here. | ||
| 843 | if (pRollbackBoundary && pRollbackBoundary->fVital) | ||
| 844 | { | ||
| 845 | *pfRollback = TRUE; | ||
| 846 | break; | ||
| 847 | } | ||
| 848 | |||
| 849 | // Move forward to next rollback boundary. | ||
| 850 | fSeekNextRollbackBoundary = TRUE; | ||
| 851 | } | ||
| 852 | } | ||
| 853 | } | ||
| 854 | |||
| 855 | if (fInTransaction) | ||
| 856 | { | ||
| 857 | LogString(REPORT_STANDARD, "Committing an MSI transaction\n"); | ||
| 858 | hr = DoMsiCommitTransaction(&context, pEngineState); | ||
| 859 | ExitOnFailure(hr, "Failed committing an MSI transaction"); | ||
| 860 | fInTransaction = FALSE; | ||
| 861 | } | ||
| 862 | |||
| 863 | LExit: | ||
| 864 | // Send execute complete to BA. | ||
| 865 | UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); | ||
| 866 | |||
| 867 | return hr; | ||
| 868 | } | ||
| 869 | |||
| 870 | extern "C" void ApplyClean( | ||
| 871 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
| 872 | __in BURN_PLAN* pPlan, | ||
| 873 | __in HANDLE hPipe | ||
| 874 | ) | ||
| 875 | { | ||
| 876 | HRESULT hr = S_OK; | ||
| 877 | |||
| 878 | for (DWORD i = 0; i < pPlan->cCleanActions; ++i) | ||
| 879 | { | ||
| 880 | BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; | ||
| 881 | |||
| 882 | hr = CleanPackage(hPipe, pCleanAction->pPackage); | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 886 | |||
| 887 | // internal helper functions | ||
| 888 | |||
| 889 | static HRESULT ExecuteDependentRegistrationActions( | ||
| 890 | __in HANDLE hPipe, | ||
| 891 | __in const BURN_REGISTRATION* pRegistration, | ||
| 892 | __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions, | ||
| 893 | __in DWORD cActions | ||
| 894 | ) | ||
| 895 | { | ||
| 896 | HRESULT hr = S_OK; | ||
| 897 | |||
| 898 | for (DWORD iAction = 0; iAction < cActions; ++iAction) | ||
| 899 | { | ||
| 900 | const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction; | ||
| 901 | |||
| 902 | if (pRegistration->fPerMachine) | ||
| 903 | { | ||
| 904 | hr = ElevationProcessDependentRegistration(hPipe, pAction); | ||
| 905 | ExitOnFailure(hr, "Failed to execute dependent registration action."); | ||
| 906 | } | ||
| 907 | else | ||
| 908 | { | ||
| 909 | hr = DependencyProcessDependentRegistration(pRegistration, pAction); | ||
| 910 | ExitOnFailure(hr, "Failed to process dependency registration action."); | ||
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | LExit: | ||
| 915 | return hr; | ||
| 916 | } | ||
| 917 | |||
| 918 | static HRESULT ExtractContainer( | ||
| 919 | __in HANDLE hSourceEngineFile, | ||
| 920 | __in BURN_CONTAINER* pContainer, | ||
| 921 | __in_z LPCWSTR wzContainerPath, | ||
| 922 | __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads, | ||
| 923 | __in DWORD cExtractPayloads | ||
| 924 | ) | ||
| 925 | { | ||
| 926 | HRESULT hr = S_OK; | ||
| 927 | BURN_CONTAINER_CONTEXT context = { }; | ||
| 928 | HANDLE hContainerHandle = INVALID_HANDLE_VALUE; | ||
| 929 | LPWSTR sczExtractPayloadId = NULL; | ||
| 930 | |||
| 931 | // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile. | ||
| 932 | if (pContainer->fActuallyAttached) | ||
| 933 | { | ||
| 934 | hContainerHandle = hSourceEngineFile; | ||
| 935 | } | ||
| 936 | |||
| 937 | hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath); | ||
| 938 | ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId); | ||
| 939 | |||
| 940 | while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId))) | ||
| 941 | { | ||
| 942 | BOOL fExtracted = FALSE; | ||
| 943 | |||
| 944 | for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract) | ||
| 945 | { | ||
| 946 | BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract; | ||
| 947 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1)) | ||
| 948 | { | ||
| 949 | // TODO: Send progress when extracting stream to file. | ||
| 950 | hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath); | ||
| 951 | ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); | ||
| 952 | |||
| 953 | fExtracted = TRUE; | ||
| 954 | break; | ||
| 955 | } | ||
| 956 | } | ||
| 957 | |||
| 958 | if (!fExtracted) | ||
| 959 | { | ||
| 960 | hr = ContainerSkipStream(&context); | ||
| 961 | ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId); | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | if (E_NOMOREITEMS == hr) | ||
| 966 | { | ||
| 967 | hr = S_OK; | ||
| 968 | } | ||
| 969 | ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId); | ||
| 970 | |||
| 971 | LExit: | ||
| 972 | ReleaseStr(sczExtractPayloadId); | ||
| 973 | ContainerClose(&context); | ||
| 974 | |||
| 975 | return hr; | ||
| 976 | } | ||
| 977 | |||
| 978 | static void UpdateCacheSuccessProgress( | ||
| 979 | __in BURN_PLAN* pPlan, | ||
| 980 | __in BURN_CACHE_ACTION* pCacheAction, | ||
| 981 | __inout DWORD64* pqwSuccessfulCachedProgress | ||
| 982 | ) | ||
| 983 | { | ||
| 984 | switch (pCacheAction->type) | ||
| 985 | { | ||
| 986 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
| 987 | *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize; | ||
| 988 | break; | ||
| 989 | |||
| 990 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
| 991 | if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply) | ||
| 992 | { | ||
| 993 | pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE; | ||
| 994 | *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize; | ||
| 995 | } | ||
| 996 | break; | ||
| 997 | |||
| 998 | case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: | ||
| 999 | if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply) | ||
| 1000 | { | ||
| 1001 | pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE; | ||
| 1002 | *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize; | ||
| 1003 | } | ||
| 1004 | break; | ||
| 1005 | |||
| 1006 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
| 1007 | if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply) | ||
| 1008 | { | ||
| 1009 | pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE; | ||
| 1010 | *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize; | ||
| 1011 | } | ||
| 1012 | break; | ||
| 1013 | |||
| 1014 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
| 1015 | if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply) | ||
| 1016 | { | ||
| 1017 | pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE; | ||
| 1018 | *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize; | ||
| 1019 | } | ||
| 1020 | break; | ||
| 1021 | |||
| 1022 | case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: | ||
| 1023 | if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply) | ||
| 1024 | { | ||
| 1025 | pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE; | ||
| 1026 | *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize; | ||
| 1027 | } | ||
| 1028 | break; | ||
| 1029 | |||
| 1030 | default: | ||
| 1031 | AssertSz(FALSE, "Unexpected cache action type."); | ||
| 1032 | break; | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | static HRESULT LayoutBundle( | ||
| 1037 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 1038 | __in BURN_VARIABLES* pVariables, | ||
| 1039 | __in HANDLE hPipe, | ||
| 1040 | __in_z LPCWSTR wzExecutableName, | ||
| 1041 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 1042 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 1043 | __in DWORD64 qwSuccessfulCacheProgress, | ||
| 1044 | __in DWORD64 qwTotalCacheSize | ||
| 1045 | ) | ||
| 1046 | { | ||
| 1047 | HRESULT hr = S_OK; | ||
| 1048 | LPWSTR sczBundlePath = NULL; | ||
| 1049 | LPWSTR sczDestinationPath = NULL; | ||
| 1050 | int nEquivalentPaths = 0; | ||
| 1051 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
| 1052 | BOOL fRetry = FALSE; | ||
| 1053 | BOOL fRetryAcquire = FALSE; | ||
| 1054 | |||
| 1055 | hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath); | ||
| 1056 | if (FAILED(hr)) | ||
| 1057 | { | ||
| 1058 | if (E_NOTFOUND != hr) | ||
| 1059 | { | ||
| 1060 | ExitOnFailure(hr, "Failed to get path to bundle source process path to layout."); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | hr = PathForCurrentProcess(&sczBundlePath, NULL); | ||
| 1064 | ExitOnFailure(hr, "Failed to get path to bundle to layout."); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath); | ||
| 1068 | ExitOnFailure(hr, "Failed to concat layout path for bundle."); | ||
| 1069 | |||
| 1070 | // If the destination path is the currently running bundle, bail. | ||
| 1071 | hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); | ||
| 1072 | ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); | ||
| 1073 | |||
| 1074 | if (CSTR_EQUAL == nEquivalentPaths) | ||
| 1075 | { | ||
| 1076 | ExitFunction1(hr = S_OK); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | progress.pUX = pUX; | ||
| 1080 | progress.qwCacheProgress = qwSuccessfulCacheProgress; | ||
| 1081 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
| 1082 | |||
| 1083 | do | ||
| 1084 | { | ||
| 1085 | hr = S_OK; | ||
| 1086 | fRetry = FALSE; | ||
| 1087 | |||
| 1088 | for (;;) | ||
| 1089 | { | ||
| 1090 | fRetryAcquire = FALSE; | ||
| 1091 | progress.fCancel = FALSE; | ||
| 1092 | |||
| 1093 | hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath); | ||
| 1094 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
| 1095 | |||
| 1096 | hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath); | ||
| 1097 | // Error handling happens after sending complete message to BA. | ||
| 1098 | |||
| 1099 | UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire); | ||
| 1100 | if (fRetryAcquire) | ||
| 1101 | { | ||
| 1102 | continue; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath); | ||
| 1106 | break; | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | do | ||
| 1110 | { | ||
| 1111 | hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL); | ||
| 1112 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
| 1113 | |||
| 1114 | if (INVALID_HANDLE_VALUE != hPipe) | ||
| 1115 | { | ||
| 1116 | hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath); | ||
| 1117 | } | ||
| 1118 | else | ||
| 1119 | { | ||
| 1120 | hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
| 1124 | UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action); | ||
| 1125 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
| 1126 | { | ||
| 1127 | hr = S_FALSE; // retry verify. | ||
| 1128 | } | ||
| 1129 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
| 1130 | { | ||
| 1131 | fRetry = TRUE; // go back and retry acquire. | ||
| 1132 | } | ||
| 1133 | } while (S_FALSE == hr); | ||
| 1134 | } while (fRetry); | ||
| 1135 | LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory); | ||
| 1136 | |||
| 1137 | LExit: | ||
| 1138 | ReleaseStr(sczDestinationPath); | ||
| 1139 | ReleaseStr(sczBundlePath); | ||
| 1140 | |||
| 1141 | return hr; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | static HRESULT AcquireContainerOrPayload( | ||
| 1145 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 1146 | __in BURN_VARIABLES* pVariables, | ||
| 1147 | __in_opt BURN_CONTAINER* pContainer, | ||
| 1148 | __in_opt BURN_PACKAGE* pPackage, | ||
| 1149 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 1150 | __in LPCWSTR wzDestinationPath, | ||
| 1151 | __in DWORD64 qwSuccessfulCacheProgress, | ||
| 1152 | __in DWORD64 qwTotalCacheSize | ||
| 1153 | ) | ||
| 1154 | { | ||
| 1155 | AssertSz(pContainer || pPayload, "Must provide a container or a payload."); | ||
| 1156 | |||
| 1157 | HRESULT hr = S_OK; | ||
| 1158 | int nEquivalentPaths = 0; | ||
| 1159 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; | ||
| 1160 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; | ||
| 1161 | LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath; | ||
| 1162 | LPWSTR sczSourceFullPath = NULL; | ||
| 1163 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
| 1164 | BOOL fRetry = FALSE; | ||
| 1165 | |||
| 1166 | progress.pContainer = pContainer; | ||
| 1167 | progress.pPackage = pPackage; | ||
| 1168 | progress.pPayload = pPayload; | ||
| 1169 | progress.pUX = pUX; | ||
| 1170 | progress.qwCacheProgress = qwSuccessfulCacheProgress; | ||
| 1171 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
| 1172 | |||
| 1173 | do | ||
| 1174 | { | ||
| 1175 | LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; | ||
| 1176 | LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath; | ||
| 1177 | |||
| 1178 | BOOL fFoundLocal = FALSE; | ||
| 1179 | BOOL fCopy = FALSE; | ||
| 1180 | BOOL fDownload = FALSE; | ||
| 1181 | |||
| 1182 | fRetry = FALSE; | ||
| 1183 | progress.fCancel = FALSE; | ||
| 1184 | |||
| 1185 | hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath); | ||
| 1186 | ExitOnFailure(hr, "Failed to search local source."); | ||
| 1187 | |||
| 1188 | if (fFoundLocal) // the file exists locally, so copy it. | ||
| 1189 | { | ||
| 1190 | // If the source path and destination path are different, do the copy (otherwise there's no point). | ||
| 1191 | hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths); | ||
| 1192 | ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path."); | ||
| 1193 | |||
| 1194 | fCopy = (CSTR_EQUAL != nEquivalentPaths); | ||
| 1195 | } | ||
| 1196 | else // can't find the file locally, so prompt for source. | ||
| 1197 | { | ||
| 1198 | DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE; | ||
| 1199 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath); | ||
| 1200 | |||
| 1201 | hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload); | ||
| 1202 | |||
| 1203 | // If the BA requested download then ensure a download url is available (it may have been set | ||
| 1204 | // during PromptForSource so we need to check again). | ||
| 1205 | if (fDownload) | ||
| 1206 | { | ||
| 1207 | wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl; | ||
| 1208 | if (!wzDownloadUrl || !*wzDownloadUrl) | ||
| 1209 | { | ||
| 1210 | hr = E_INVALIDARG; | ||
| 1211 | } | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | // Log the error | ||
| 1215 | LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | if (fCopy) | ||
| 1219 | { | ||
| 1220 | hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath); | ||
| 1221 | ExitOnRootFailure(hr, "BA aborted cache acquire begin."); | ||
| 1222 | |||
| 1223 | hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath); | ||
| 1224 | // Error handling happens after sending complete message to BA. | ||
| 1225 | |||
| 1226 | // We successfully copied from a source location, set that as the last used source. | ||
| 1227 | if (SUCCEEDED(hr)) | ||
| 1228 | { | ||
| 1229 | CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath); | ||
| 1230 | } | ||
| 1231 | } | ||
| 1232 | else if (fDownload) | ||
| 1233 | { | ||
| 1234 | hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl); | ||
| 1235 | ExitOnRootFailure(hr, "BA aborted cache download payload begin."); | ||
| 1236 | |||
| 1237 | hr = DownloadPayload(&progress, wzDestinationPath); | ||
| 1238 | // Error handling happens after sending complete message to BA. | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | if (fCopy || fDownload) | ||
| 1242 | { | ||
| 1243 | UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry); | ||
| 1244 | if (fRetry) | ||
| 1245 | { | ||
| 1246 | hr = S_OK; | ||
| 1247 | } | ||
| 1248 | } | ||
| 1249 | ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath); | ||
| 1250 | } while (fRetry); | ||
| 1251 | ExitOnFailure(hr, "Failed to find external payload to cache."); | ||
| 1252 | |||
| 1253 | LExit: | ||
| 1254 | ReleaseStr(sczSourceFullPath); | ||
| 1255 | |||
| 1256 | return hr; | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | static HRESULT LayoutOrCacheContainerOrPayload( | ||
| 1260 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 1261 | __in HANDLE hPipe, | ||
| 1262 | __in_opt BURN_CONTAINER* pContainer, | ||
| 1263 | __in_opt BURN_PACKAGE* pPackage, | ||
| 1264 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 1265 | __in BOOL fAlreadyProvidedProgress, | ||
| 1266 | __in DWORD64 qwSuccessfulCachedProgress, | ||
| 1267 | __in DWORD64 qwTotalCacheSize, | ||
| 1268 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 1269 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 1270 | __in BOOL fMove, | ||
| 1271 | __in DWORD cTryAgainAttempts, | ||
| 1272 | __out BOOL* pfRetry | ||
| 1273 | ) | ||
| 1274 | { | ||
| 1275 | HRESULT hr = S_OK; | ||
| 1276 | LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L""; | ||
| 1277 | LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L""; | ||
| 1278 | LARGE_INTEGER liContainerOrPayloadSize = { }; | ||
| 1279 | LARGE_INTEGER liZero = { }; | ||
| 1280 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { }; | ||
| 1281 | |||
| 1282 | liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize; | ||
| 1283 | |||
| 1284 | progress.pContainer = pContainer; | ||
| 1285 | progress.pPackage = pPackage; | ||
| 1286 | progress.pPayload = pPayload; | ||
| 1287 | progress.pUX = pUX; | ||
| 1288 | progress.qwTotalCacheSize = qwTotalCacheSize; | ||
| 1289 | if (fAlreadyProvidedProgress) | ||
| 1290 | { | ||
| 1291 | Assert(qwSuccessfulCachedProgress >= static_cast<DWORD64>(liContainerOrPayloadSize.QuadPart)); | ||
| 1292 | progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already. | ||
| 1293 | } | ||
| 1294 | else | ||
| 1295 | { | ||
| 1296 | progress.qwCacheProgress = qwSuccessfulCachedProgress; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | *pfRetry = FALSE; | ||
| 1300 | |||
| 1301 | do | ||
| 1302 | { | ||
| 1303 | hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId); | ||
| 1304 | ExitOnRootFailure(hr, "BA aborted cache verify begin."); | ||
| 1305 | |||
| 1306 | if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process. | ||
| 1307 | { | ||
| 1308 | hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
| 1309 | } | ||
| 1310 | else if (wzLayoutDirectory) // layout the container or payload. | ||
| 1311 | { | ||
| 1312 | if (pContainer) | ||
| 1313 | { | ||
| 1314 | hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
| 1315 | } | ||
| 1316 | else | ||
| 1317 | { | ||
| 1318 | hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | else // complete the payload. | ||
| 1322 | { | ||
| 1323 | Assert(!pContainer); | ||
| 1324 | Assert(pPackage); | ||
| 1325 | |||
| 1326 | hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA | ||
| 1330 | // will get. | ||
| 1331 | if (SUCCEEDED(hr)) | ||
| 1332 | { | ||
| 1333 | CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress); | ||
| 1334 | if (progress.fCancel) | ||
| 1335 | { | ||
| 1336 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1337 | } | ||
| 1338 | else if (progress.fError) | ||
| 1339 | { | ||
| 1340 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
| 1341 | } | ||
| 1342 | ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE; | ||
| 1346 | UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action); | ||
| 1347 | if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action) | ||
| 1348 | { | ||
| 1349 | hr = S_FALSE; // retry verify. | ||
| 1350 | } | ||
| 1351 | else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action) | ||
| 1352 | { | ||
| 1353 | *pfRetry = TRUE; // go back and retry acquire. | ||
| 1354 | } | ||
| 1355 | } while (S_FALSE == hr); | ||
| 1356 | |||
| 1357 | LExit: | ||
| 1358 | return hr; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | static HRESULT PromptForSource( | ||
| 1362 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 1363 | __in_z LPCWSTR wzPackageOrContainerId, | ||
| 1364 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 1365 | __in_z LPCWSTR wzLocalSource, | ||
| 1366 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 1367 | __inout BOOL* pfRetry, | ||
| 1368 | __inout BOOL* pfDownload | ||
| 1369 | ) | ||
| 1370 | { | ||
| 1371 | HRESULT hr = S_OK; | ||
| 1372 | BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE; | ||
| 1373 | |||
| 1374 | UserExperienceDeactivateEngine(pUX); | ||
| 1375 | |||
| 1376 | hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action); | ||
| 1377 | if (FAILED(hr)) | ||
| 1378 | { | ||
| 1379 | ExitFunction(); | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | switch (action) | ||
| 1383 | { | ||
| 1384 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE: | ||
| 1385 | hr = E_FILENOTFOUND; | ||
| 1386 | break; | ||
| 1387 | |||
| 1388 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY: | ||
| 1389 | *pfRetry = TRUE; | ||
| 1390 | break; | ||
| 1391 | |||
| 1392 | case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD: | ||
| 1393 | *pfDownload = TRUE; | ||
| 1394 | break; | ||
| 1395 | |||
| 1396 | default: | ||
| 1397 | hr = E_INVALIDARG; | ||
| 1398 | break; | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | LExit: | ||
| 1402 | UserExperienceActivateEngine(pUX, NULL); | ||
| 1403 | return hr; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | static HRESULT CopyPayload( | ||
| 1407 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
| 1408 | __in_z LPCWSTR wzSourcePath, | ||
| 1409 | __in_z LPCWSTR wzDestinationPath | ||
| 1410 | ) | ||
| 1411 | { | ||
| 1412 | HRESULT hr = S_OK; | ||
| 1413 | DWORD dwFileAttributes = 0; | ||
| 1414 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
| 1415 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; | ||
| 1416 | |||
| 1417 | DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
| 1418 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath); | ||
| 1419 | |||
| 1420 | // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. | ||
| 1421 | if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) | ||
| 1422 | { | ||
| 1423 | if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) | ||
| 1424 | { | ||
| 1425 | dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; | ||
| 1426 | if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) | ||
| 1427 | { | ||
| 1428 | ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0)) | ||
| 1434 | { | ||
| 1435 | if (pProgress->fCancel) | ||
| 1436 | { | ||
| 1437 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1438 | ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
| 1439 | } | ||
| 1440 | else | ||
| 1441 | { | ||
| 1442 | ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath); | ||
| 1443 | } | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | LExit: | ||
| 1447 | return hr; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | static HRESULT DownloadPayload( | ||
| 1451 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
| 1452 | __in_z LPCWSTR wzDestinationPath | ||
| 1453 | ) | ||
| 1454 | { | ||
| 1455 | HRESULT hr = S_OK; | ||
| 1456 | DWORD dwFileAttributes = 0; | ||
| 1457 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L""; | ||
| 1458 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L""; | ||
| 1459 | DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource; | ||
| 1460 | DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize; | ||
| 1461 | DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; | ||
| 1462 | DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; | ||
| 1463 | APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; | ||
| 1464 | |||
| 1465 | DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD; | ||
| 1466 | LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl); | ||
| 1467 | |||
| 1468 | // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED. | ||
| 1469 | if (FileExistsEx(wzDestinationPath, &dwFileAttributes)) | ||
| 1470 | { | ||
| 1471 | if (FILE_ATTRIBUTE_READONLY & dwFileAttributes) | ||
| 1472 | { | ||
| 1473 | dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; | ||
| 1474 | if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes)) | ||
| 1475 | { | ||
| 1476 | ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath); | ||
| 1477 | } | ||
| 1478 | } | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | cacheCallback.pfnProgress = CacheProgressRoutine; | ||
| 1482 | cacheCallback.pfnCancel = NULL; // TODO: set this | ||
| 1483 | cacheCallback.pv = pProgress; | ||
| 1484 | |||
| 1485 | // If the protocol is specially marked, "bits" let's use that. | ||
| 1486 | if (L'b' == pDownloadSource->sczUrl[0] && | ||
| 1487 | L'i' == pDownloadSource->sczUrl[1] && | ||
| 1488 | L't' == pDownloadSource->sczUrl[2] && | ||
| 1489 | L's' == pDownloadSource->sczUrl[3] && | ||
| 1490 | (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5])) | ||
| 1491 | ) | ||
| 1492 | { | ||
| 1493 | hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath); | ||
| 1494 | } | ||
| 1495 | else // wininet handles everything else. | ||
| 1496 | { | ||
| 1497 | authenticationData.pUX = pProgress->pUX; | ||
| 1498 | authenticationData.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 1499 | authenticationData.wzPayloadId = wzPayloadId; | ||
| 1500 | authenticationCallback.pv = static_cast<LPVOID>(&authenticationData); | ||
| 1501 | authenticationCallback.pfnAuthenticate = &AuthenticationRequired; | ||
| 1502 | |||
| 1503 | hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback); | ||
| 1504 | } | ||
| 1505 | ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath); | ||
| 1506 | |||
| 1507 | LExit: | ||
| 1508 | return hr; | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | static HRESULT WINAPI AuthenticationRequired( | ||
| 1512 | __in LPVOID pData, | ||
| 1513 | __in HINTERNET hUrl, | ||
| 1514 | __in long lHttpCode, | ||
| 1515 | __out BOOL* pfRetrySend, | ||
| 1516 | __out BOOL* pfRetry | ||
| 1517 | ) | ||
| 1518 | { | ||
| 1519 | Assert(401 == lHttpCode || 407 == lHttpCode); | ||
| 1520 | |||
| 1521 | HRESULT hr = S_OK; | ||
| 1522 | DWORD er = ERROR_SUCCESS; | ||
| 1523 | BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; | ||
| 1524 | LPWSTR sczError = NULL; | ||
| 1525 | int nResult = IDNOACTION; | ||
| 1526 | |||
| 1527 | *pfRetrySend = FALSE; | ||
| 1528 | *pfRetry = FALSE; | ||
| 1529 | |||
| 1530 | hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); | ||
| 1531 | ExitOnFailure(hr, "Failed to allocation error string."); | ||
| 1532 | |||
| 1533 | APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData); | ||
| 1534 | |||
| 1535 | UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value; | ||
| 1536 | nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); | ||
| 1537 | if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply) | ||
| 1538 | { | ||
| 1539 | er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); | ||
| 1540 | if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) | ||
| 1541 | { | ||
| 1542 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1543 | } | ||
| 1544 | else if (ERROR_INTERNET_FORCE_RETRY == er) | ||
| 1545 | { | ||
| 1546 | *pfRetrySend = TRUE; | ||
| 1547 | hr = S_OK; | ||
| 1548 | } | ||
| 1549 | else | ||
| 1550 | { | ||
| 1551 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | else if (IDRETRY == nResult) | ||
| 1555 | { | ||
| 1556 | *pfRetry = TRUE; | ||
| 1557 | hr = S_OK; | ||
| 1558 | } | ||
| 1559 | else | ||
| 1560 | { | ||
| 1561 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | LExit: | ||
| 1565 | ReleaseStr(sczError); | ||
| 1566 | |||
| 1567 | return hr; | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | static DWORD CALLBACK CacheProgressRoutine( | ||
| 1571 | __in LARGE_INTEGER TotalFileSize, | ||
| 1572 | __in LARGE_INTEGER TotalBytesTransferred, | ||
| 1573 | __in LARGE_INTEGER /*StreamSize*/, | ||
| 1574 | __in LARGE_INTEGER /*StreamBytesTransferred*/, | ||
| 1575 | __in DWORD /*dwStreamNumber*/, | ||
| 1576 | __in DWORD /*dwCallbackReason*/, | ||
| 1577 | __in HANDLE /*hSourceFile*/, | ||
| 1578 | __in HANDLE /*hDestinationFile*/, | ||
| 1579 | __in_opt LPVOID lpData | ||
| 1580 | ) | ||
| 1581 | { | ||
| 1582 | HRESULT hr = S_OK; | ||
| 1583 | DWORD dwResult = PROGRESS_CONTINUE; | ||
| 1584 | BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT*>(lpData); | ||
| 1585 | LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL; | ||
| 1586 | LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL; | ||
| 1587 | DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart; | ||
| 1588 | if (qwCacheProgress > pProgress->qwTotalCacheSize) | ||
| 1589 | { | ||
| 1590 | AssertSz(FALSE, "Apply has cached more than Plan envisioned."); | ||
| 1591 | qwCacheProgress = pProgress->qwTotalCacheSize; | ||
| 1592 | } | ||
| 1593 | DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0; | ||
| 1594 | |||
| 1595 | hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage); | ||
| 1596 | if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) | ||
| 1597 | { | ||
| 1598 | dwResult = PROGRESS_CANCEL; | ||
| 1599 | pProgress->fCancel = TRUE; | ||
| 1600 | } | ||
| 1601 | else if (FAILED(hr)) | ||
| 1602 | { | ||
| 1603 | dwResult = PROGRESS_CANCEL; | ||
| 1604 | pProgress->fError = TRUE; | ||
| 1605 | } | ||
| 1606 | else | ||
| 1607 | { | ||
| 1608 | dwResult = PROGRESS_CONTINUE; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | return dwResult; | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | static void DoRollbackCache( | ||
| 1615 | __in BURN_USER_EXPERIENCE* /*pUX*/, | ||
| 1616 | __in BURN_PLAN* pPlan, | ||
| 1617 | __in HANDLE hPipe, | ||
| 1618 | __in DWORD dwCheckpoint | ||
| 1619 | ) | ||
| 1620 | { | ||
| 1621 | HRESULT hr = S_OK; | ||
| 1622 | DWORD iCheckpoint = 0; | ||
| 1623 | |||
| 1624 | // Scan to last checkpoint. | ||
| 1625 | for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) | ||
| 1626 | { | ||
| 1627 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
| 1628 | |||
| 1629 | if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint) | ||
| 1630 | { | ||
| 1631 | iCheckpoint = i; | ||
| 1632 | break; | ||
| 1633 | } | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | // Rollback cache actions. | ||
| 1637 | if (iCheckpoint) | ||
| 1638 | { | ||
| 1639 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
| 1640 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
| 1641 | { | ||
| 1642 | BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i]; | ||
| 1643 | |||
| 1644 | switch (pRollbackCacheAction->type) | ||
| 1645 | { | ||
| 1646 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
| 1647 | break; | ||
| 1648 | |||
| 1649 | case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: | ||
| 1650 | hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage); | ||
| 1651 | break; | ||
| 1652 | |||
| 1653 | default: | ||
| 1654 | AssertSz(FALSE, "Invalid rollback cache action."); | ||
| 1655 | break; | ||
| 1656 | } | ||
| 1657 | } | ||
| 1658 | } | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | /* MSI Transactions: | ||
| 1662 | * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together. | ||
| 1663 | */ | ||
| 1664 | static HRESULT ExecuteMsiBeginTransaction( | ||
| 1665 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 1666 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1667 | ) | ||
| 1668 | { | ||
| 1669 | HRESULT hr = S_OK; | ||
| 1670 | UINT uResult = ERROR_SUCCESS; | ||
| 1671 | |||
| 1672 | // Per user/machine context | ||
| 1673 | if (pEngineState->plan.fPerMachine) | ||
| 1674 | { | ||
| 1675 | hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
| 1676 | ExitOnFailure(hr, "Failed to begin an MSI transaction."); | ||
| 1677 | } | ||
| 1678 | else | ||
| 1679 | { | ||
| 1680 | MSIHANDLE hMsiTrns = NULL; | ||
| 1681 | HANDLE hMsiTrnsEvent = NULL; | ||
| 1682 | uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent); | ||
| 1683 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | LExit: | ||
| 1687 | return hr; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | static HRESULT ExecuteMsiCommitTransaction( | ||
| 1691 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 1692 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1693 | ) | ||
| 1694 | { | ||
| 1695 | HRESULT hr = S_OK; | ||
| 1696 | UINT uResult = ERROR_SUCCESS; | ||
| 1697 | |||
| 1698 | // Per user/machine context | ||
| 1699 | if (pEngineState->plan.fPerMachine) | ||
| 1700 | { | ||
| 1701 | hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
| 1702 | ExitOnFailure(hr, "Failed to commit an MSI transaction."); | ||
| 1703 | } | ||
| 1704 | else | ||
| 1705 | { | ||
| 1706 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); | ||
| 1707 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | LExit: | ||
| 1711 | return hr; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | static HRESULT ExecuteMsiRollbackTransaction( | ||
| 1715 | __in BURN_EXECUTE_CONTEXT* pContext | ||
| 1716 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1717 | ) | ||
| 1718 | { | ||
| 1719 | HRESULT hr = S_OK; | ||
| 1720 | UINT uResult = ERROR_SUCCESS; | ||
| 1721 | |||
| 1722 | // Per user/machine context | ||
| 1723 | if (pEngineState->plan.fPerMachine) | ||
| 1724 | { | ||
| 1725 | hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext); | ||
| 1726 | ExitOnFailure(hr, "Failed to rollback an MSI transaction."); | ||
| 1727 | } | ||
| 1728 | else | ||
| 1729 | { | ||
| 1730 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); | ||
| 1731 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | LExit: | ||
| 1735 | return hr; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | // Currently, supporting only elevated transactions. | ||
| 1739 | static HRESULT DoMsiBeginTransaction( | ||
| 1740 | __in BURN_EXECUTE_CONTEXT *pContext | ||
| 1741 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1742 | ) | ||
| 1743 | { | ||
| 1744 | HRESULT hr = S_OK; | ||
| 1745 | |||
| 1746 | hr = ExecuteMsiBeginTransaction(pContext, pEngineState); | ||
| 1747 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
| 1748 | |||
| 1749 | LExit: | ||
| 1750 | return hr; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | static HRESULT DoMsiCommitTransaction( | ||
| 1754 | __in BURN_EXECUTE_CONTEXT *pContext | ||
| 1755 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1756 | ) | ||
| 1757 | { | ||
| 1758 | HRESULT hr = S_OK; | ||
| 1759 | |||
| 1760 | hr = ExecuteMsiCommitTransaction(pContext, pEngineState); | ||
| 1761 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
| 1762 | |||
| 1763 | LExit: | ||
| 1764 | return hr; | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | static HRESULT DoMsiRollbackTransaction( | ||
| 1768 | __in BURN_EXECUTE_CONTEXT *pContext | ||
| 1769 | , __in BURN_ENGINE_STATE* pEngineState | ||
| 1770 | ) | ||
| 1771 | { | ||
| 1772 | HRESULT hr = S_OK; | ||
| 1773 | |||
| 1774 | hr = ExecuteMsiRollbackTransaction(pContext, pEngineState); | ||
| 1775 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
| 1776 | |||
| 1777 | LExit: | ||
| 1778 | return hr; | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | |||
| 1782 | static HRESULT DoExecuteAction( | ||
| 1783 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 1784 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 1785 | __in_opt HANDLE hCacheThread, | ||
| 1786 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 1787 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
| 1788 | __out DWORD* pdwCheckpoint, | ||
| 1789 | __out BOOL* pfKeepRegistration, | ||
| 1790 | __out BOOL* pfSuspend, | ||
| 1791 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 1792 | ) | ||
| 1793 | { | ||
| 1794 | Assert(!pExecuteAction->fDeleted); | ||
| 1795 | |||
| 1796 | HRESULT hr = S_OK; | ||
| 1797 | HANDLE rghWait[2] = { }; | ||
| 1798 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 1799 | BOOL fRetry = FALSE; | ||
| 1800 | BOOL fStopWusaService = FALSE; | ||
| 1801 | |||
| 1802 | pContext->fRollback = FALSE; | ||
| 1803 | |||
| 1804 | do | ||
| 1805 | { | ||
| 1806 | switch (pExecuteAction->type) | ||
| 1807 | { | ||
| 1808 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
| 1809 | *pdwCheckpoint = pExecuteAction->checkpoint.dwId; | ||
| 1810 | break; | ||
| 1811 | |||
| 1812 | case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: | ||
| 1813 | // wait for cache sync-point | ||
| 1814 | rghWait[0] = pExecuteAction->syncpoint.hEvent; | ||
| 1815 | rghWait[1] = hCacheThread; | ||
| 1816 | switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE)) | ||
| 1817 | { | ||
| 1818 | case WAIT_OBJECT_0: | ||
| 1819 | break; | ||
| 1820 | |||
| 1821 | case WAIT_OBJECT_0 + 1: | ||
| 1822 | if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) | ||
| 1823 | { | ||
| 1824 | ExitWithLastError(hr, "Failed to get cache thread exit code."); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | if (SUCCEEDED(hr)) | ||
| 1828 | { | ||
| 1829 | hr = E_UNEXPECTED; | ||
| 1830 | } | ||
| 1831 | ExitOnFailure(hr, "Cache thread exited unexpectedly."); | ||
| 1832 | |||
| 1833 | case WAIT_FAILED: __fallthrough; | ||
| 1834 | default: | ||
| 1835 | ExitWithLastError(hr, "Failed to wait for cache check-point."); | ||
| 1836 | } | ||
| 1837 | break; | ||
| 1838 | |||
| 1839 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
| 1840 | hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
| 1841 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
| 1842 | break; | ||
| 1843 | |||
| 1844 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
| 1845 | hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
| 1846 | ExitOnFailure(hr, "Failed to execute MSI package."); | ||
| 1847 | break; | ||
| 1848 | |||
| 1849 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
| 1850 | hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart); | ||
| 1851 | ExitOnFailure(hr, "Failed to execute MSP package."); | ||
| 1852 | break; | ||
| 1853 | |||
| 1854 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
| 1855 | hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart); | ||
| 1856 | fStopWusaService = fRetry; | ||
| 1857 | ExitOnFailure(hr, "Failed to execute MSU package."); | ||
| 1858 | break; | ||
| 1859 | |||
| 1860 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
| 1861 | hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext); | ||
| 1862 | ExitOnFailure(hr, "Failed to execute package provider registration action."); | ||
| 1863 | break; | ||
| 1864 | |||
| 1865 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
| 1866 | hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext); | ||
| 1867 | ExitOnFailure(hr, "Failed to execute dependency action."); | ||
| 1868 | break; | ||
| 1869 | |||
| 1870 | case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: | ||
| 1871 | hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction); | ||
| 1872 | ExitOnFailure(hr, "Failed to execute compatible package action."); | ||
| 1873 | break; | ||
| 1874 | |||
| 1875 | case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: | ||
| 1876 | *pfKeepRegistration = pExecuteAction->registration.fKeep; | ||
| 1877 | break; | ||
| 1878 | |||
| 1879 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
| 1880 | *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary; | ||
| 1881 | break; | ||
| 1882 | |||
| 1883 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; | ||
| 1884 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; | ||
| 1885 | default: | ||
| 1886 | hr = E_UNEXPECTED; | ||
| 1887 | ExitOnFailure(hr, "Invalid execute action."); | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | if (*pRestart < restart) | ||
| 1891 | { | ||
| 1892 | *pRestart = restart; | ||
| 1893 | } | ||
| 1894 | } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED); | ||
| 1895 | |||
| 1896 | LExit: | ||
| 1897 | return hr; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | static HRESULT DoRollbackActions( | ||
| 1901 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 1902 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 1903 | __in DWORD dwCheckpoint, | ||
| 1904 | __in BOOL fInTransaction, | ||
| 1905 | __out BOOL* pfKeepRegistration, | ||
| 1906 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 1907 | ) | ||
| 1908 | { | ||
| 1909 | HRESULT hr = S_OK; | ||
| 1910 | DWORD iCheckpoint = 0; | ||
| 1911 | BOOL fRetryIgnored = FALSE; | ||
| 1912 | BOOL fSuspendIgnored = FALSE; | ||
| 1913 | |||
| 1914 | pContext->fRollback = TRUE; | ||
| 1915 | |||
| 1916 | // Rollback MSI transaction | ||
| 1917 | if (fInTransaction) | ||
| 1918 | { | ||
| 1919 | hr = DoMsiRollbackTransaction(pContext, pEngineState); | ||
| 1920 | ExitOnFailure(hr, "Failed rolling back transaction"); | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | // scan to last checkpoint | ||
| 1924 | for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i) | ||
| 1925 | { | ||
| 1926 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
| 1927 | if (pRollbackAction->fDeleted) | ||
| 1928 | { | ||
| 1929 | continue; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type) | ||
| 1933 | { | ||
| 1934 | if (pRollbackAction->checkpoint.dwId == dwCheckpoint) | ||
| 1935 | { | ||
| 1936 | iCheckpoint = i; | ||
| 1937 | break; | ||
| 1938 | } | ||
| 1939 | } | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | // execute rollback actions | ||
| 1943 | if (iCheckpoint) | ||
| 1944 | { | ||
| 1945 | // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF. | ||
| 1946 | for (int i = iCheckpoint - 1; i >= 0; --i) | ||
| 1947 | { | ||
| 1948 | BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i]; | ||
| 1949 | if (pRollbackAction->fDeleted) | ||
| 1950 | { | ||
| 1951 | continue; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 1955 | switch (pRollbackAction->type) | ||
| 1956 | { | ||
| 1957 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
| 1958 | break; | ||
| 1959 | |||
| 1960 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
| 1961 | hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
| 1962 | TraceError(hr, "Failed to rollback EXE package."); | ||
| 1963 | hr = S_OK; | ||
| 1964 | break; | ||
| 1965 | |||
| 1966 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
| 1967 | if (fInTransaction) | ||
| 1968 | { | ||
| 1969 | LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n"); | ||
| 1970 | break; | ||
| 1971 | } | ||
| 1972 | hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
| 1973 | TraceError(hr, "Failed to rollback MSI package."); | ||
| 1974 | hr = S_OK; | ||
| 1975 | break; | ||
| 1976 | |||
| 1977 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
| 1978 | if (fInTransaction) | ||
| 1979 | { | ||
| 1980 | LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n"); | ||
| 1981 | break; | ||
| 1982 | } | ||
| 1983 | hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
| 1984 | TraceError(hr, "Failed to rollback MSP package."); | ||
| 1985 | hr = S_OK; | ||
| 1986 | break; | ||
| 1987 | |||
| 1988 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
| 1989 | if (fInTransaction) | ||
| 1990 | { | ||
| 1991 | LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n"); | ||
| 1992 | break; | ||
| 1993 | } | ||
| 1994 | hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart); | ||
| 1995 | TraceError(hr, "Failed to rollback MSU package."); | ||
| 1996 | hr = S_OK; | ||
| 1997 | break; | ||
| 1998 | |||
| 1999 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
| 2000 | hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext); | ||
| 2001 | TraceError(hr, "Failed to rollback package provider action."); | ||
| 2002 | hr = S_OK; | ||
| 2003 | break; | ||
| 2004 | |||
| 2005 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
| 2006 | hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext); | ||
| 2007 | TraceError(hr, "Failed to rollback dependency action."); | ||
| 2008 | hr = S_OK; | ||
| 2009 | break; | ||
| 2010 | |||
| 2011 | case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: | ||
| 2012 | *pfKeepRegistration = pRollbackAction->registration.fKeep; | ||
| 2013 | break; | ||
| 2014 | |||
| 2015 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
| 2016 | ExitFunction1(hr = S_OK); | ||
| 2017 | |||
| 2018 | case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: | ||
| 2019 | hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage); | ||
| 2020 | break; | ||
| 2021 | |||
| 2022 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; | ||
| 2023 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough; | ||
| 2024 | default: | ||
| 2025 | hr = E_UNEXPECTED; | ||
| 2026 | ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type); | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | if (*pRestart < restart) | ||
| 2030 | { | ||
| 2031 | *pRestart = restart; | ||
| 2032 | } | ||
| 2033 | } | ||
| 2034 | } | ||
| 2035 | |||
| 2036 | LExit: | ||
| 2037 | return hr; | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | static HRESULT ExecuteExePackage( | ||
| 2041 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2042 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 2043 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 2044 | __in BOOL fRollback, | ||
| 2045 | __out BOOL* pfRetry, | ||
| 2046 | __out BOOL* pfSuspend, | ||
| 2047 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 2048 | ) | ||
| 2049 | { | ||
| 2050 | HRESULT hr = S_OK; | ||
| 2051 | HRESULT hrExecute = S_OK; | ||
| 2052 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 2053 | int nResult = 0; | ||
| 2054 | BOOL fBeginCalled = FALSE; | ||
| 2055 | |||
| 2056 | if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult)) | ||
| 2057 | { | ||
| 2058 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult); | ||
| 2059 | ExitFunction1(hr = S_OK); | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | Assert(pContext->fRollback == fRollback); | ||
| 2063 | pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage; | ||
| 2064 | fBeginCalled = TRUE; | ||
| 2065 | |||
| 2066 | // Send package execute begin to BA. | ||
| 2067 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback); | ||
| 2068 | ExitOnRootFailure(hr, "BA aborted execute EXE package begin."); | ||
| 2069 | |||
| 2070 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 2071 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 2072 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
| 2073 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
| 2074 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
| 2075 | ExitOnRootFailure(hr, "UX aborted EXE progress."); | ||
| 2076 | |||
| 2077 | // Execute package. | ||
| 2078 | if (pExecuteAction->exePackage.pPackage->fPerMachine) | ||
| 2079 | { | ||
| 2080 | hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
| 2081 | ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package."); | ||
| 2082 | } | ||
| 2083 | else | ||
| 2084 | { | ||
| 2085 | hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | ||
| 2086 | ExitOnFailure(hrExecute, "Failed to configure per-user EXE package."); | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 2090 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 2091 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
| 2092 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
| 2093 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
| 2094 | ExitOnRootFailure(hr, "UX aborted EXE progress."); | ||
| 2095 | |||
| 2096 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
| 2097 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
| 2098 | |||
| 2099 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
| 2100 | ExitOnRootFailure(hr, "UX aborted EXE package execute progress."); | ||
| 2101 | |||
| 2102 | LExit: | ||
| 2103 | if (fBeginCalled) | ||
| 2104 | { | ||
| 2105 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | return hr; | ||
| 2109 | } | ||
| 2110 | |||
| 2111 | static HRESULT ExecuteMsiPackage( | ||
| 2112 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2113 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 2114 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 2115 | __in BOOL fRollback, | ||
| 2116 | __out BOOL* pfRetry, | ||
| 2117 | __out BOOL* pfSuspend, | ||
| 2118 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 2119 | ) | ||
| 2120 | { | ||
| 2121 | HRESULT hr = S_OK; | ||
| 2122 | HRESULT hrExecute = S_OK; | ||
| 2123 | BOOL fBeginCalled = FALSE; | ||
| 2124 | |||
| 2125 | if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult)) | ||
| 2126 | { | ||
| 2127 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult); | ||
| 2128 | ExitFunction1(hr = S_OK); | ||
| 2129 | } | ||
| 2130 | |||
| 2131 | Assert(pContext->fRollback == fRollback); | ||
| 2132 | pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage; | ||
| 2133 | fBeginCalled = TRUE; | ||
| 2134 | |||
| 2135 | // Send package execute begin to BA. | ||
| 2136 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback); | ||
| 2137 | ExitOnRootFailure(hr, "BA aborted execute MSI package begin."); | ||
| 2138 | |||
| 2139 | // execute package | ||
| 2140 | if (pExecuteAction->msiPackage.pPackage->fPerMachine) | ||
| 2141 | { | ||
| 2142 | hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
| 2143 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package."); | ||
| 2144 | } | ||
| 2145 | else | ||
| 2146 | { | ||
| 2147 | hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
| 2148 | ExitOnFailure(hrExecute, "Failed to configure per-user MSI package."); | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
| 2152 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
| 2153 | |||
| 2154 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
| 2155 | ExitOnRootFailure(hr, "UX aborted MSI package execute progress."); | ||
| 2156 | |||
| 2157 | LExit: | ||
| 2158 | if (fBeginCalled) | ||
| 2159 | { | ||
| 2160 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | return hr; | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | static HRESULT ExecuteMspPackage( | ||
| 2167 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2168 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 2169 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 2170 | __in BOOL fRollback, | ||
| 2171 | __out BOOL* pfRetry, | ||
| 2172 | __out BOOL* pfSuspend, | ||
| 2173 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 2174 | ) | ||
| 2175 | { | ||
| 2176 | HRESULT hr = S_OK; | ||
| 2177 | HRESULT hrExecute = S_OK; | ||
| 2178 | BOOL fBeginCalled = FALSE; | ||
| 2179 | |||
| 2180 | if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult)) | ||
| 2181 | { | ||
| 2182 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult); | ||
| 2183 | ExitFunction1(hr = S_OK); | ||
| 2184 | } | ||
| 2185 | |||
| 2186 | Assert(pContext->fRollback == fRollback); | ||
| 2187 | pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage; | ||
| 2188 | fBeginCalled = TRUE; | ||
| 2189 | |||
| 2190 | // Send package execute begin to BA. | ||
| 2191 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback); | ||
| 2192 | ExitOnRootFailure(hr, "BA aborted execute MSP package begin."); | ||
| 2193 | |||
| 2194 | // Now send all the patches that target this product code. | ||
| 2195 | for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) | ||
| 2196 | { | ||
| 2197 | BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; | ||
| 2198 | |||
| 2199 | hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode); | ||
| 2200 | ExitOnRootFailure(hr, "BA aborted execute MSP target."); | ||
| 2201 | } | ||
| 2202 | |||
| 2203 | // execute package | ||
| 2204 | if (pExecuteAction->mspTarget.fPerMachineTarget) | ||
| 2205 | { | ||
| 2206 | hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
| 2207 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package."); | ||
| 2208 | } | ||
| 2209 | else | ||
| 2210 | { | ||
| 2211 | hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart); | ||
| 2212 | ExitOnFailure(hrExecute, "Failed to configure per-user MSP package."); | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
| 2216 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
| 2217 | |||
| 2218 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
| 2219 | ExitOnRootFailure(hr, "UX aborted MSP package execute progress."); | ||
| 2220 | |||
| 2221 | LExit: | ||
| 2222 | if (fBeginCalled) | ||
| 2223 | { | ||
| 2224 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | return hr; | ||
| 2228 | } | ||
| 2229 | |||
| 2230 | static HRESULT ExecuteMsuPackage( | ||
| 2231 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2232 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 2233 | __in BURN_EXECUTE_CONTEXT* pContext, | ||
| 2234 | __in BOOL fRollback, | ||
| 2235 | __in BOOL fStopWusaService, | ||
| 2236 | __out BOOL* pfRetry, | ||
| 2237 | __out BOOL* pfSuspend, | ||
| 2238 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 2239 | ) | ||
| 2240 | { | ||
| 2241 | HRESULT hr = S_OK; | ||
| 2242 | HRESULT hrExecute = S_OK; | ||
| 2243 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 2244 | int nResult = 0; | ||
| 2245 | BOOL fBeginCalled = FALSE; | ||
| 2246 | |||
| 2247 | if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult)) | ||
| 2248 | { | ||
| 2249 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult); | ||
| 2250 | ExitFunction1(hr = S_OK); | ||
| 2251 | } | ||
| 2252 | |||
| 2253 | Assert(pContext->fRollback == fRollback); | ||
| 2254 | pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage; | ||
| 2255 | fBeginCalled = TRUE; | ||
| 2256 | |||
| 2257 | // Send package execute begin to BA. | ||
| 2258 | hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback); | ||
| 2259 | ExitOnRootFailure(hr, "BA aborted execute MSU package begin."); | ||
| 2260 | |||
| 2261 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 2262 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 2263 | message.progress.dwPercentage = fRollback ? 100 : 0; | ||
| 2264 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
| 2265 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
| 2266 | ExitOnRootFailure(hr, "UX aborted MSU progress."); | ||
| 2267 | |||
| 2268 | // execute package | ||
| 2269 | if (pExecuteAction->msuPackage.pPackage->fPerMachine) | ||
| 2270 | { | ||
| 2271 | hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart); | ||
| 2272 | ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package."); | ||
| 2273 | } | ||
| 2274 | else | ||
| 2275 | { | ||
| 2276 | hrExecute = E_UNEXPECTED; | ||
| 2277 | ExitOnFailure(hr, "MSU packages cannot be per-user."); | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 2281 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 2282 | message.progress.dwPercentage = fRollback ? 0 : 100; | ||
| 2283 | nResult = GenericExecuteMessageHandler(&message, pContext); | ||
| 2284 | hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult); | ||
| 2285 | ExitOnRootFailure(hr, "UX aborted MSU progress."); | ||
| 2286 | |||
| 2287 | pContext->cExecutedPackages += fRollback ? -1 : 1; | ||
| 2288 | (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1; | ||
| 2289 | |||
| 2290 | hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks); | ||
| 2291 | ExitOnRootFailure(hr, "UX aborted MSU package execute progress."); | ||
| 2292 | |||
| 2293 | LExit: | ||
| 2294 | if (fBeginCalled) | ||
| 2295 | { | ||
| 2296 | hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); | ||
| 2297 | } | ||
| 2298 | |||
| 2299 | return hr; | ||
| 2300 | } | ||
| 2301 | |||
| 2302 | static HRESULT ExecutePackageProviderAction( | ||
| 2303 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2304 | __in BURN_EXECUTE_ACTION* pAction, | ||
| 2305 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
| 2306 | ) | ||
| 2307 | { | ||
| 2308 | HRESULT hr = S_OK; | ||
| 2309 | |||
| 2310 | if (pAction->packageProvider.pPackage->fPerMachine) | ||
| 2311 | { | ||
| 2312 | hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction); | ||
| 2313 | ExitOnFailure(hr, "Failed to register the package provider on per-machine package."); | ||
| 2314 | } | ||
| 2315 | else | ||
| 2316 | { | ||
| 2317 | hr = DependencyExecutePackageProviderAction(pAction); | ||
| 2318 | ExitOnFailure(hr, "Failed to register the package provider on per-user package."); | ||
| 2319 | } | ||
| 2320 | |||
| 2321 | LExit: | ||
| 2322 | return hr; | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | static HRESULT ExecuteDependencyAction( | ||
| 2326 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2327 | __in BURN_EXECUTE_ACTION* pAction, | ||
| 2328 | __in BURN_EXECUTE_CONTEXT* /*pContext*/ | ||
| 2329 | ) | ||
| 2330 | { | ||
| 2331 | HRESULT hr = S_OK; | ||
| 2332 | |||
| 2333 | if (pAction->packageDependency.pPackage->fPerMachine) | ||
| 2334 | { | ||
| 2335 | hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction); | ||
| 2336 | ExitOnFailure(hr, "Failed to register the dependency on per-machine package."); | ||
| 2337 | } | ||
| 2338 | else | ||
| 2339 | { | ||
| 2340 | hr = DependencyExecutePackageDependencyAction(FALSE, pAction); | ||
| 2341 | ExitOnFailure(hr, "Failed to register the dependency on per-user package."); | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | LExit: | ||
| 2345 | return hr; | ||
| 2346 | } | ||
| 2347 | |||
| 2348 | static HRESULT ExecuteCompatiblePackageAction( | ||
| 2349 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 2350 | __in BURN_EXECUTE_ACTION* pAction | ||
| 2351 | ) | ||
| 2352 | { | ||
| 2353 | HRESULT hr = S_OK; | ||
| 2354 | |||
| 2355 | if (pAction->compatiblePackage.pReferencePackage->fPerMachine) | ||
| 2356 | { | ||
| 2357 | hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction); | ||
| 2358 | ExitOnFailure(hr, "Failed to load compatible package on per-machine package."); | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | // Compatible package already loaded in this process. | ||
| 2362 | |||
| 2363 | LExit: | ||
| 2364 | return hr; | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | static HRESULT CleanPackage( | ||
| 2368 | __in HANDLE hElevatedPipe, | ||
| 2369 | __in BURN_PACKAGE* pPackage | ||
| 2370 | ) | ||
| 2371 | { | ||
| 2372 | HRESULT hr = S_OK; | ||
| 2373 | |||
| 2374 | if (pPackage->fPerMachine) | ||
| 2375 | { | ||
| 2376 | hr = ElevationCleanPackage(hElevatedPipe, pPackage); | ||
| 2377 | } | ||
| 2378 | else | ||
| 2379 | { | ||
| 2380 | hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId); | ||
| 2381 | } | ||
| 2382 | |||
| 2383 | return hr; | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | static int GenericExecuteMessageHandler( | ||
| 2387 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
| 2388 | __in LPVOID pvContext | ||
| 2389 | ) | ||
| 2390 | { | ||
| 2391 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
| 2392 | int nResult = IDNOACTION; | ||
| 2393 | |||
| 2394 | switch (pMessage->type) | ||
| 2395 | { | ||
| 2396 | case GENERIC_EXECUTE_MESSAGE_PROGRESS: | ||
| 2397 | { | ||
| 2398 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
| 2399 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
| 2400 | } | ||
| 2401 | break; | ||
| 2402 | |||
| 2403 | case GENERIC_EXECUTE_MESSAGE_ERROR: | ||
| 2404 | UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value. | ||
| 2405 | break; | ||
| 2406 | |||
| 2407 | case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: | ||
| 2408 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value. | ||
| 2409 | break; | ||
| 2410 | } | ||
| 2411 | |||
| 2412 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
| 2413 | return nResult; | ||
| 2414 | } | ||
| 2415 | |||
| 2416 | static int MsiExecuteMessageHandler( | ||
| 2417 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
| 2418 | __in_opt LPVOID pvContext | ||
| 2419 | ) | ||
| 2420 | { | ||
| 2421 | BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext; | ||
| 2422 | int nResult = IDNOACTION; | ||
| 2423 | |||
| 2424 | switch (pMessage->type) | ||
| 2425 | { | ||
| 2426 | case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: | ||
| 2427 | { | ||
| 2428 | DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; | ||
| 2429 | UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. | ||
| 2430 | } | ||
| 2431 | break; | ||
| 2432 | |||
| 2433 | case WIU_MSI_EXECUTE_MESSAGE_ERROR: | ||
| 2434 | nResult = pMessage->nResultRecommendation; | ||
| 2435 | UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. | ||
| 2436 | break; | ||
| 2437 | |||
| 2438 | case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: | ||
| 2439 | nResult = pMessage->nResultRecommendation; | ||
| 2440 | UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. | ||
| 2441 | break; | ||
| 2442 | |||
| 2443 | case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: | ||
| 2444 | UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value. | ||
| 2445 | break; | ||
| 2446 | } | ||
| 2447 | |||
| 2448 | nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult); | ||
| 2449 | return nResult; | ||
| 2450 | } | ||
| 2451 | |||
| 2452 | static HRESULT ReportOverallProgressTicks( | ||
| 2453 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 2454 | __in BOOL fRollback, | ||
| 2455 | __in DWORD cOverallProgressTicksTotal, | ||
| 2456 | __in DWORD cOverallProgressTicks | ||
| 2457 | ) | ||
| 2458 | { | ||
| 2459 | HRESULT hr = S_OK; | ||
| 2460 | DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0; | ||
| 2461 | |||
| 2462 | // TODO: consider sending different progress numbers in the future. | ||
| 2463 | hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress); | ||
| 2464 | |||
| 2465 | return hr; | ||
| 2466 | } | ||
| 2467 | |||
| 2468 | static HRESULT ExecutePackageComplete( | ||
| 2469 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 2470 | __in BURN_VARIABLES* pVariables, | ||
| 2471 | __in BURN_PACKAGE* pPackage, | ||
| 2472 | __in HRESULT hrOverall, | ||
| 2473 | __in HRESULT hrExecute, | ||
| 2474 | __in BOOL fRollback, | ||
| 2475 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart, | ||
| 2476 | __out BOOL* pfRetry, | ||
| 2477 | __out BOOL* pfSuspend | ||
| 2478 | ) | ||
| 2479 | { | ||
| 2480 | HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. | ||
| 2481 | BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; | ||
| 2482 | |||
| 2483 | // Send package execute complete to BA. | ||
| 2484 | UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); | ||
| 2485 | if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) | ||
| 2486 | { | ||
| 2487 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
| 2488 | } | ||
| 2489 | *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures. | ||
| 2490 | *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction); | ||
| 2491 | |||
| 2492 | // Remember this package as the package that initiated the forced restart. | ||
| 2493 | if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) | ||
| 2494 | { | ||
| 2495 | // Best effort to set the forced restart package variable. | ||
| 2496 | VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE); | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | // If we're retrying, leave a message in the log file and say everything is okay. | ||
| 2500 | if (*pfRetry) | ||
| 2501 | { | ||
| 2502 | LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); | ||
| 2503 | hr = S_OK; | ||
| 2504 | } | ||
| 2505 | else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. | ||
| 2506 | { | ||
| 2507 | LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); | ||
| 2508 | hr = S_OK; | ||
| 2509 | } | ||
| 2510 | else | ||
| 2511 | { | ||
| 2512 | LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); | ||
| 2513 | } | ||
| 2514 | |||
| 2515 | return hr; | ||
| 2516 | } | ||
diff --git a/src/engine/apply.h b/src/engine/apply.h new file mode 100644 index 00000000..b717251e --- /dev/null +++ b/src/engine/apply.h | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | enum GENERIC_EXECUTE_MESSAGE_TYPE | ||
| 11 | { | ||
| 12 | GENERIC_EXECUTE_MESSAGE_NONE, | ||
| 13 | GENERIC_EXECUTE_MESSAGE_ERROR, | ||
| 14 | GENERIC_EXECUTE_MESSAGE_PROGRESS, | ||
| 15 | GENERIC_EXECUTE_MESSAGE_FILES_IN_USE, | ||
| 16 | }; | ||
| 17 | |||
| 18 | typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA | ||
| 19 | { | ||
| 20 | BURN_USER_EXPERIENCE* pUX; | ||
| 21 | LPCWSTR wzPackageOrContainerId; | ||
| 22 | LPCWSTR wzPayloadId; | ||
| 23 | } APPLY_AUTHENTICATION_REQUIRED_DATA; | ||
| 24 | |||
| 25 | typedef struct _GENERIC_EXECUTE_MESSAGE | ||
| 26 | { | ||
| 27 | GENERIC_EXECUTE_MESSAGE_TYPE type; | ||
| 28 | DWORD dwAllowedResults; | ||
| 29 | |||
| 30 | union | ||
| 31 | { | ||
| 32 | struct | ||
| 33 | { | ||
| 34 | DWORD dwErrorCode; | ||
| 35 | LPCWSTR wzMessage; | ||
| 36 | } error; | ||
| 37 | struct | ||
| 38 | { | ||
| 39 | DWORD dwPercentage; | ||
| 40 | } progress; | ||
| 41 | struct | ||
| 42 | { | ||
| 43 | DWORD cFiles; | ||
| 44 | LPCWSTR* rgwzFiles; | ||
| 45 | } filesInUse; | ||
| 46 | }; | ||
| 47 | } GENERIC_EXECUTE_MESSAGE; | ||
| 48 | |||
| 49 | |||
| 50 | typedef int (*PFN_GENERICMESSAGEHANDLER)( | ||
| 51 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
| 52 | __in LPVOID pvContext | ||
| 53 | ); | ||
| 54 | |||
| 55 | |||
| 56 | void ApplyInitialize(); | ||
| 57 | void ApplyUninitialize(); | ||
| 58 | HRESULT ApplySetVariables( | ||
| 59 | __in BURN_VARIABLES* pVariables | ||
| 60 | ); | ||
| 61 | void ApplyReset( | ||
| 62 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 63 | __in BURN_PACKAGES* pPackages | ||
| 64 | ); | ||
| 65 | HRESULT ApplyLock( | ||
| 66 | __in BOOL fPerMachine, | ||
| 67 | __out HANDLE* phLock | ||
| 68 | ); | ||
| 69 | HRESULT ApplyRegister( | ||
| 70 | __in BURN_ENGINE_STATE* pEngineState | ||
| 71 | ); | ||
| 72 | HRESULT ApplyUnregister( | ||
| 73 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 74 | __in BOOL fFailedOrRollback, | ||
| 75 | __in BOOL fRollback, | ||
| 76 | __in BOOL fSuspend, | ||
| 77 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
| 78 | ); | ||
| 79 | HRESULT ApplyCache( | ||
| 80 | __in HANDLE hSourceEngineFile, | ||
| 81 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 82 | __in BURN_VARIABLES* pVariables, | ||
| 83 | __in BURN_PLAN* pPlan, | ||
| 84 | __in HANDLE hPipe, | ||
| 85 | __inout DWORD* pcOverallProgressTicks, | ||
| 86 | __out BOOL* pfRollback | ||
| 87 | ); | ||
| 88 | HRESULT ApplyExecute( | ||
| 89 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 90 | __in_opt HANDLE hCacheThread, | ||
| 91 | __inout DWORD* pcOverallProgressTicks, | ||
| 92 | __out BOOL* pfKeepRegistration, | ||
| 93 | __out BOOL* pfRollback, | ||
| 94 | __out BOOL* pfSuspend, | ||
| 95 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 96 | ); | ||
| 97 | void ApplyClean( | ||
| 98 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 99 | __in BURN_PLAN* pPlan, | ||
| 100 | __in HANDLE hPipe | ||
| 101 | ); | ||
| 102 | |||
| 103 | |||
| 104 | #ifdef __cplusplus | ||
| 105 | } | ||
| 106 | #endif | ||
diff --git a/src/engine/approvedexe.cpp b/src/engine/approvedexe.cpp new file mode 100644 index 00000000..55518519 --- /dev/null +++ b/src/engine/approvedexe.cpp | |||
| @@ -0,0 +1,262 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // function definitions | ||
| 7 | |||
| 8 | extern "C" HRESULT ApprovedExesParseFromXml( | ||
| 9 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 10 | __in IXMLDOMNode* pixnBundle | ||
| 11 | ) | ||
| 12 | { | ||
| 13 | HRESULT hr = S_OK; | ||
| 14 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 15 | IXMLDOMNode* pixnNode = NULL; | ||
| 16 | DWORD cNodes = 0; | ||
| 17 | LPWSTR scz = NULL; | ||
| 18 | |||
| 19 | // select approved exe nodes | ||
| 20 | hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes); | ||
| 21 | ExitOnFailure(hr, "Failed to select approved exe nodes."); | ||
| 22 | |||
| 23 | // get approved exe node count | ||
| 24 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 25 | ExitOnFailure(hr, "Failed to get approved exe node count."); | ||
| 26 | |||
| 27 | if (!cNodes) | ||
| 28 | { | ||
| 29 | ExitFunction(); | ||
| 30 | } | ||
| 31 | |||
| 32 | // allocate memory for approved exes | ||
| 33 | pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE); | ||
| 34 | ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs."); | ||
| 35 | |||
| 36 | pApprovedExes->cApprovedExes = cNodes; | ||
| 37 | |||
| 38 | // parse approved exe elements | ||
| 39 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 40 | { | ||
| 41 | BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; | ||
| 42 | |||
| 43 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 44 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 45 | |||
| 46 | // @Id | ||
| 47 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId); | ||
| 48 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 49 | |||
| 50 | // @Key | ||
| 51 | hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey); | ||
| 52 | ExitOnFailure(hr, "Failed to get @Key."); | ||
| 53 | |||
| 54 | // @ValueName | ||
| 55 | hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName); | ||
| 56 | if (E_NOTFOUND != hr) | ||
| 57 | { | ||
| 58 | ExitOnFailure(hr, "Failed to get @ValueName."); | ||
| 59 | } | ||
| 60 | |||
| 61 | // @Win64 | ||
| 62 | hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64); | ||
| 63 | if (E_NOTFOUND != hr) | ||
| 64 | { | ||
| 65 | ExitOnFailure(hr, "Failed to get @Win64."); | ||
| 66 | } | ||
| 67 | |||
| 68 | // prepare next iteration | ||
| 69 | ReleaseNullObject(pixnNode); | ||
| 70 | ReleaseNullStr(scz); | ||
| 71 | } | ||
| 72 | |||
| 73 | hr = S_OK; | ||
| 74 | |||
| 75 | LExit: | ||
| 76 | ReleaseObject(pixnNodes); | ||
| 77 | ReleaseObject(pixnNode); | ||
| 78 | ReleaseStr(scz); | ||
| 79 | return hr; | ||
| 80 | } | ||
| 81 | |||
| 82 | extern "C" void ApprovedExesUninitialize( | ||
| 83 | __in BURN_APPROVED_EXES* pApprovedExes | ||
| 84 | ) | ||
| 85 | { | ||
| 86 | if (pApprovedExes->rgApprovedExes) | ||
| 87 | { | ||
| 88 | for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) | ||
| 89 | { | ||
| 90 | BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i]; | ||
| 91 | |||
| 92 | ReleaseStr(pApprovedExe->sczId); | ||
| 93 | ReleaseStr(pApprovedExe->sczKey); | ||
| 94 | ReleaseStr(pApprovedExe->sczValueName); | ||
| 95 | } | ||
| 96 | MemFree(pApprovedExes->rgApprovedExes); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | extern "C" void ApprovedExesUninitializeLaunch( | ||
| 101 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 102 | ) | ||
| 103 | { | ||
| 104 | if (pLaunchApprovedExe) | ||
| 105 | { | ||
| 106 | ReleaseStr(pLaunchApprovedExe->sczArguments); | ||
| 107 | ReleaseStr(pLaunchApprovedExe->sczExecutablePath); | ||
| 108 | ReleaseStr(pLaunchApprovedExe->sczId); | ||
| 109 | MemFree(pLaunchApprovedExe); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | extern "C" HRESULT ApprovedExesFindById( | ||
| 114 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 115 | __in_z LPCWSTR wzId, | ||
| 116 | __out BURN_APPROVED_EXE** ppApprovedExe | ||
| 117 | ) | ||
| 118 | { | ||
| 119 | HRESULT hr = S_OK; | ||
| 120 | BURN_APPROVED_EXE* pApprovedExe = NULL; | ||
| 121 | |||
| 122 | for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i) | ||
| 123 | { | ||
| 124 | pApprovedExe = &pApprovedExes->rgApprovedExes[i]; | ||
| 125 | |||
| 126 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1)) | ||
| 127 | { | ||
| 128 | *ppApprovedExe = pApprovedExe; | ||
| 129 | ExitFunction1(hr = S_OK); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | hr = E_NOTFOUND; | ||
| 134 | |||
| 135 | LExit: | ||
| 136 | return hr; | ||
| 137 | } | ||
| 138 | |||
| 139 | extern "C" HRESULT ApprovedExesLaunch( | ||
| 140 | __in BURN_VARIABLES* pVariables, | ||
| 141 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
| 142 | __out DWORD* pdwProcessId | ||
| 143 | ) | ||
| 144 | { | ||
| 145 | HRESULT hr = S_OK; | ||
| 146 | LPWSTR sczArgumentsFormatted = NULL; | ||
| 147 | LPWSTR sczArgumentsObfuscated = NULL; | ||
| 148 | LPWSTR sczCommand = NULL; | ||
| 149 | LPWSTR sczCommandObfuscated = NULL; | ||
| 150 | LPWSTR sczExecutableDirectory = NULL; | ||
| 151 | STARTUPINFOW si = { }; | ||
| 152 | PROCESS_INFORMATION pi = { }; | ||
| 153 | |||
| 154 | // build command | ||
| 155 | if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) | ||
| 156 | { | ||
| 157 | hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); | ||
| 158 | ExitOnFailure(hr, "Failed to format argument string."); | ||
| 159 | |||
| 160 | hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); | ||
| 161 | ExitOnFailure(hr, "Failed to create executable command."); | ||
| 162 | |||
| 163 | hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); | ||
| 164 | ExitOnFailure(hr, "Failed to format obfuscated argument string."); | ||
| 165 | |||
| 166 | hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); | ||
| 167 | } | ||
| 168 | else | ||
| 169 | { | ||
| 170 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); | ||
| 171 | ExitOnFailure(hr, "Failed to create executable command."); | ||
| 172 | |||
| 173 | hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); | ||
| 174 | } | ||
| 175 | ExitOnFailure(hr, "Failed to create obfuscated executable command."); | ||
| 176 | |||
| 177 | // Try to get the directory of the executable so we can set the current directory of the process to help those executables | ||
| 178 | // that expect stuff to be relative to them. Best effort only. | ||
| 179 | hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); | ||
| 180 | if (FAILED(hr)) | ||
| 181 | { | ||
| 182 | ReleaseNullStr(sczExecutableDirectory); | ||
| 183 | } | ||
| 184 | |||
| 185 | LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); | ||
| 186 | |||
| 187 | si.cb = sizeof(si); | ||
| 188 | if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) | ||
| 189 | { | ||
| 190 | ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); | ||
| 191 | } | ||
| 192 | |||
| 193 | *pdwProcessId = pi.dwProcessId; | ||
| 194 | |||
| 195 | if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) | ||
| 196 | { | ||
| 197 | ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); | ||
| 198 | } | ||
| 199 | |||
| 200 | LExit: | ||
| 201 | StrSecureZeroFreeString(sczArgumentsFormatted); | ||
| 202 | ReleaseStr(sczArgumentsObfuscated); | ||
| 203 | StrSecureZeroFreeString(sczCommand); | ||
| 204 | ReleaseStr(sczCommandObfuscated); | ||
| 205 | ReleaseStr(sczExecutableDirectory); | ||
| 206 | |||
| 207 | ReleaseHandle(pi.hThread); | ||
| 208 | ReleaseHandle(pi.hProcess); | ||
| 209 | |||
| 210 | return hr; | ||
| 211 | } | ||
| 212 | |||
| 213 | extern "C" HRESULT ApprovedExesVerifySecureLocation( | ||
| 214 | __in BURN_VARIABLES* pVariables, | ||
| 215 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 216 | ) | ||
| 217 | { | ||
| 218 | HRESULT hr = S_OK; | ||
| 219 | LPWSTR scz = NULL; | ||
| 220 | |||
| 221 | const LPCWSTR vrgSecureFolderVariables[] = { | ||
| 222 | L"ProgramFiles64Folder", | ||
| 223 | L"ProgramFilesFolder", | ||
| 224 | }; | ||
| 225 | |||
| 226 | for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i) | ||
| 227 | { | ||
| 228 | LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i]; | ||
| 229 | |||
| 230 | hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz); | ||
| 231 | if (SUCCEEDED(hr)) | ||
| 232 | { | ||
| 233 | hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); | ||
| 234 | if (S_OK == hr) | ||
| 235 | { | ||
| 236 | ExitFunction(); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | else if (E_NOTFOUND != hr) | ||
| 240 | { | ||
| 241 | ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | // The problem with using a Variable for the root package cache folder is that it might not have been secured yet. | ||
| 246 | // Getting it through CacheGetRootCompletedPath makes sure it has been secured. | ||
| 247 | hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz); | ||
| 248 | ExitOnFailure(hr, "Failed to get the root package cache folder."); | ||
| 249 | |||
| 250 | hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath); | ||
| 251 | if (S_OK == hr) | ||
| 252 | { | ||
| 253 | ExitFunction(); | ||
| 254 | } | ||
| 255 | |||
| 256 | hr = S_FALSE; | ||
| 257 | |||
| 258 | LExit: | ||
| 259 | ReleaseStr(scz); | ||
| 260 | |||
| 261 | return hr; | ||
| 262 | } | ||
diff --git a/src/engine/approvedexe.h b/src/engine/approvedexe.h new file mode 100644 index 00000000..23f3b1bb --- /dev/null +++ b/src/engine/approvedexe.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // structs | ||
| 11 | |||
| 12 | typedef struct _BURN_APPROVED_EXE | ||
| 13 | { | ||
| 14 | LPWSTR sczId; | ||
| 15 | LPWSTR sczKey; | ||
| 16 | LPWSTR sczValueName; | ||
| 17 | BOOL fWin64; | ||
| 18 | } BURN_APPROVED_EXE; | ||
| 19 | |||
| 20 | typedef struct _BURN_APPROVED_EXES | ||
| 21 | { | ||
| 22 | BURN_APPROVED_EXE* rgApprovedExes; | ||
| 23 | DWORD cApprovedExes; | ||
| 24 | } BURN_APPROVED_EXES; | ||
| 25 | |||
| 26 | typedef struct _BURN_LAUNCH_APPROVED_EXE | ||
| 27 | { | ||
| 28 | HWND hwndParent; | ||
| 29 | LPWSTR sczId; | ||
| 30 | LPWSTR sczExecutablePath; | ||
| 31 | LPWSTR sczArguments; | ||
| 32 | DWORD dwWaitForInputIdleTimeout; | ||
| 33 | } BURN_LAUNCH_APPROVED_EXE; | ||
| 34 | |||
| 35 | |||
| 36 | // function declarations | ||
| 37 | |||
| 38 | HRESULT ApprovedExesParseFromXml( | ||
| 39 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 40 | __in IXMLDOMNode* pixnBundle | ||
| 41 | ); | ||
| 42 | |||
| 43 | void ApprovedExesUninitialize( | ||
| 44 | __in BURN_APPROVED_EXES* pApprovedExes | ||
| 45 | ); | ||
| 46 | void ApprovedExesUninitializeLaunch( | ||
| 47 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 48 | ); | ||
| 49 | HRESULT ApprovedExesFindById( | ||
| 50 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 51 | __in_z LPCWSTR wzId, | ||
| 52 | __out BURN_APPROVED_EXE** ppApprovedExe | ||
| 53 | ); | ||
| 54 | HRESULT ApprovedExesLaunch( | ||
| 55 | __in BURN_VARIABLES* pVariables, | ||
| 56 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
| 57 | __out DWORD* pdwProcessId | ||
| 58 | ); | ||
| 59 | HRESULT ApprovedExesVerifySecureLocation( | ||
| 60 | __in BURN_VARIABLES* pVariables, | ||
| 61 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 62 | ); | ||
| 63 | |||
| 64 | |||
| 65 | #if defined(__cplusplus) | ||
| 66 | } | ||
| 67 | #endif | ||
diff --git a/src/engine/bitsengine.cpp b/src/engine/bitsengine.cpp new file mode 100644 index 00000000..b8093b77 --- /dev/null +++ b/src/engine/bitsengine.cpp | |||
| @@ -0,0 +1,505 @@ | |||
| 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 | |||
| 5 | // const | ||
| 6 | |||
| 7 | const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60; | ||
| 8 | const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1; | ||
| 9 | |||
| 10 | // functions | ||
| 11 | |||
| 12 | static HRESULT CreateJob( | ||
| 13 | __out IBackgroundCopyJob** ppJob | ||
| 14 | ); | ||
| 15 | static HRESULT SetCredentials( | ||
| 16 | __in IBackgroundCopyJob* pJob, | ||
| 17 | __in_z_opt LPCWSTR wzUser, | ||
| 18 | __in_z_opt LPCWSTR wzPassword | ||
| 19 | ); | ||
| 20 | static void SendError( | ||
| 21 | __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, | ||
| 22 | __in IBackgroundCopyJob* pJob, | ||
| 23 | __in HRESULT hrError, | ||
| 24 | __in BG_ERROR_CONTEXT context, | ||
| 25 | __out_opt BOOL* pfRetry | ||
| 26 | ); | ||
| 27 | |||
| 28 | |||
| 29 | // class | ||
| 30 | |||
| 31 | class CBurnBitsCallback : public IBackgroundCopyCallback | ||
| 32 | { | ||
| 33 | public: // IUnknown | ||
| 34 | virtual STDMETHODIMP QueryInterface( | ||
| 35 | __in const IID& riid, | ||
| 36 | __out void** ppvObject | ||
| 37 | ) | ||
| 38 | { | ||
| 39 | HRESULT hr = S_OK; | ||
| 40 | |||
| 41 | ExitOnNull(ppvObject, hr, E_INVALIDARG, "Invalid argument ppvObject"); | ||
| 42 | *ppvObject = NULL; | ||
| 43 | |||
| 44 | if (::IsEqualIID(__uuidof(IBackgroundCopyCallback), riid)) | ||
| 45 | { | ||
| 46 | *ppvObject = static_cast<IBackgroundCopyCallback*>(this); | ||
| 47 | } | ||
| 48 | else if (::IsEqualIID(IID_IUnknown, riid)) | ||
| 49 | { | ||
| 50 | *ppvObject = reinterpret_cast<IUnknown*>(this); | ||
| 51 | } | ||
| 52 | else // no interface for requested iid | ||
| 53 | { | ||
| 54 | ExitFunction1(hr = E_NOINTERFACE); | ||
| 55 | } | ||
| 56 | |||
| 57 | AddRef(); | ||
| 58 | |||
| 59 | LExit: | ||
| 60 | return hr; | ||
| 61 | } | ||
| 62 | |||
| 63 | virtual STDMETHODIMP_(ULONG) AddRef() | ||
| 64 | { | ||
| 65 | return ::InterlockedIncrement(&this->m_cReferences); | ||
| 66 | } | ||
| 67 | |||
| 68 | virtual STDMETHODIMP_(ULONG) Release() | ||
| 69 | { | ||
| 70 | long l = ::InterlockedDecrement(&this->m_cReferences); | ||
| 71 | if (0 < l) | ||
| 72 | { | ||
| 73 | return l; | ||
| 74 | } | ||
| 75 | |||
| 76 | delete this; | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | public: // IBackgroundCopyCallback | ||
| 81 | virtual STDMETHODIMP JobTransferred( | ||
| 82 | __in IBackgroundCopyJob* pJob | ||
| 83 | ) | ||
| 84 | { | ||
| 85 | HRESULT hr = S_OK; | ||
| 86 | |||
| 87 | hr = SendProgress(pJob); | ||
| 88 | ExitOnFailure(hr, "Failure while sending progress during BITS job transferred."); | ||
| 89 | |||
| 90 | LExit: | ||
| 91 | if (FAILED(hr)) | ||
| 92 | { | ||
| 93 | ProcessResult(BG_ERROR_CONTEXT_NONE, hr); | ||
| 94 | } | ||
| 95 | else | ||
| 96 | { | ||
| 97 | ::SetEvent(m_hComplete); | ||
| 98 | } | ||
| 99 | |||
| 100 | return S_OK; // must return S_OK otherwise BITS just keeps calling back. | ||
| 101 | } | ||
| 102 | |||
| 103 | virtual STDMETHODIMP JobError( | ||
| 104 | __in IBackgroundCopyJob* /*pJob*/, | ||
| 105 | __in IBackgroundCopyError* pError | ||
| 106 | ) | ||
| 107 | { | ||
| 108 | HRESULT hr = S_OK; | ||
| 109 | BG_ERROR_CONTEXT context = BG_ERROR_CONTEXT_NONE; | ||
| 110 | HRESULT hrError = S_OK; | ||
| 111 | |||
| 112 | hr = pError->GetError(&context, &hrError); | ||
| 113 | ExitOnFailure(hr, "Failed to get error context."); | ||
| 114 | |||
| 115 | if (SUCCEEDED(hrError)) | ||
| 116 | { | ||
| 117 | hr = E_UNEXPECTED; | ||
| 118 | } | ||
| 119 | |||
| 120 | LExit: | ||
| 121 | ProcessResult(context, FAILED(hrError) ? hrError : hr); | ||
| 122 | |||
| 123 | return S_OK; // must return S_OK otherwise BITS just keeps calling back. | ||
| 124 | } | ||
| 125 | |||
| 126 | virtual STDMETHODIMP JobModification( | ||
| 127 | __in IBackgroundCopyJob* pJob, | ||
| 128 | __in DWORD /*dwReserved*/ | ||
| 129 | ) | ||
| 130 | { | ||
| 131 | HRESULT hr = S_OK; | ||
| 132 | BG_JOB_STATE state = BG_JOB_STATE_ERROR; | ||
| 133 | |||
| 134 | ::EnterCriticalSection(&m_cs); | ||
| 135 | |||
| 136 | hr = pJob->GetState(&state); | ||
| 137 | ExitOnFailure(hr, "Failed to get state during job modification."); | ||
| 138 | |||
| 139 | // If we're actually downloading stuff, let's send progress. | ||
| 140 | if (BG_JOB_STATE_TRANSFERRING == state) | ||
| 141 | { | ||
| 142 | hr = SendProgress(pJob); | ||
| 143 | ExitOnFailure(hr, "Failure while sending progress during BITS job modification."); | ||
| 144 | } | ||
| 145 | |||
| 146 | LExit: | ||
| 147 | ::LeaveCriticalSection(&m_cs); | ||
| 148 | |||
| 149 | ProcessResult(BG_ERROR_CONTEXT_NONE, hr); | ||
| 150 | |||
| 151 | return S_OK; // documentation says to always return S_OK | ||
| 152 | } | ||
| 153 | |||
| 154 | public: | ||
| 155 | void Reset() | ||
| 156 | { | ||
| 157 | m_hrError = S_OK; | ||
| 158 | m_contextError = BG_ERROR_CONTEXT_NONE; | ||
| 159 | |||
| 160 | ::ResetEvent(m_hComplete); | ||
| 161 | } | ||
| 162 | |||
| 163 | HRESULT WaitForCompletion( | ||
| 164 | __in IBackgroundCopyJob* pJob | ||
| 165 | ) | ||
| 166 | { | ||
| 167 | HRESULT hr = S_OK; | ||
| 168 | HANDLE rghEvents[1] = { m_hComplete }; | ||
| 169 | MSG msg = { }; | ||
| 170 | BOOL fMessageProcessed = FALSE; | ||
| 171 | |||
| 172 | do | ||
| 173 | { | ||
| 174 | fMessageProcessed = FALSE; | ||
| 175 | |||
| 176 | switch (::MsgWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, BITSENGINE_MSG_WAIT_TIMEOUT * 1000, QS_ALLINPUT)) | ||
| 177 | { | ||
| 178 | case WAIT_OBJECT_0: | ||
| 179 | break; | ||
| 180 | |||
| 181 | case WAIT_OBJECT_0 + 1: | ||
| 182 | ::PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE); | ||
| 183 | fMessageProcessed = TRUE; | ||
| 184 | break; | ||
| 185 | |||
| 186 | case WAIT_TIMEOUT: | ||
| 187 | // Call the progress callback periodically if we are not transferring to ensure that cancelling is responsive | ||
| 188 | // (progress callback is also handles cancelling). Note that if we are transferring, IBackgroundCopyCallback | ||
| 189 | // methods handle progress/cancelling. If we are not transferring, the IBackgroundCopyCallback methods may | ||
| 190 | // not be called until the job times out (minutes for a foreground job, weeks for a background job). | ||
| 191 | SendProgressIfNotTransferring(pJob); | ||
| 192 | fMessageProcessed = TRUE; | ||
| 193 | break; | ||
| 194 | |||
| 195 | default: | ||
| 196 | ExitWithLastError(hr, "Failed while waiting for download."); | ||
| 197 | } | ||
| 198 | } while (fMessageProcessed); | ||
| 199 | |||
| 200 | LExit: | ||
| 201 | return hr; | ||
| 202 | } | ||
| 203 | |||
| 204 | void GetError( | ||
| 205 | __out HRESULT* pHR, | ||
| 206 | __out BG_ERROR_CONTEXT* pContext | ||
| 207 | ) | ||
| 208 | { | ||
| 209 | *pHR = m_hrError; | ||
| 210 | *pContext = m_contextError; | ||
| 211 | } | ||
| 212 | |||
| 213 | private: | ||
| 214 | HRESULT SendProgress( | ||
| 215 | __in IBackgroundCopyJob* pJob | ||
| 216 | ) | ||
| 217 | { | ||
| 218 | HRESULT hr = S_OK; | ||
| 219 | BG_JOB_PROGRESS progress = { }; | ||
| 220 | |||
| 221 | if (m_pCallback && m_pCallback->pfnProgress) | ||
| 222 | { | ||
| 223 | hr = pJob->GetProgress(&progress); | ||
| 224 | ExitOnFailure(hr, "Failed to get progress when BITS job was transferred."); | ||
| 225 | |||
| 226 | hr = CacheSendProgressCallback(m_pCallback, progress.BytesTransferred, progress.BytesTotal, INVALID_HANDLE_VALUE); | ||
| 227 | ExitOnFailure(hr, "Failed to send progress from BITS job."); | ||
| 228 | } | ||
| 229 | |||
| 230 | LExit: | ||
| 231 | return hr; | ||
| 232 | } | ||
| 233 | |||
| 234 | void SendProgressIfNotTransferring( | ||
| 235 | __in IBackgroundCopyJob* pJob | ||
| 236 | ) | ||
| 237 | { | ||
| 238 | HRESULT hr = S_OK; | ||
| 239 | BG_JOB_STATE state = BG_JOB_STATE_ERROR; | ||
| 240 | |||
| 241 | ::EnterCriticalSection(&m_cs); | ||
| 242 | |||
| 243 | hr = pJob->GetState(&state); | ||
| 244 | ExitOnFailure(hr, "Failed to get BITS job state."); | ||
| 245 | |||
| 246 | if (BG_JOB_STATE_TRANSFERRING != state) | ||
| 247 | { | ||
| 248 | hr = SendProgress(pJob); | ||
| 249 | ExitOnFailure(hr, "Failure while sending progress."); | ||
| 250 | } | ||
| 251 | |||
| 252 | LExit: | ||
| 253 | ::LeaveCriticalSection(&m_cs); | ||
| 254 | |||
| 255 | ProcessResult(BG_ERROR_CONTEXT_NONE, hr); | ||
| 256 | } | ||
| 257 | |||
| 258 | void ProcessResult( | ||
| 259 | __in BG_ERROR_CONTEXT context, | ||
| 260 | __in HRESULT hr | ||
| 261 | ) | ||
| 262 | { | ||
| 263 | if (FAILED(hr)) | ||
| 264 | { | ||
| 265 | m_contextError = context; | ||
| 266 | m_hrError = hr; | ||
| 267 | |||
| 268 | ::SetEvent(m_hComplete); | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | public: | ||
| 273 | CBurnBitsCallback( | ||
| 274 | __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 275 | __out HRESULT* pHR | ||
| 276 | ) | ||
| 277 | { | ||
| 278 | HRESULT hr = S_OK; | ||
| 279 | |||
| 280 | m_cReferences = 1; | ||
| 281 | ::InitializeCriticalSection(&m_cs); | ||
| 282 | |||
| 283 | m_hComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 284 | ExitOnNullWithLastError(m_hComplete, hr, "Failed to create BITS job complete event."); | ||
| 285 | |||
| 286 | m_contextError = BG_ERROR_CONTEXT_NONE; | ||
| 287 | m_hrError = S_OK; | ||
| 288 | |||
| 289 | m_pCallback = pCallback; | ||
| 290 | |||
| 291 | LExit: | ||
| 292 | *pHR = hr; | ||
| 293 | } | ||
| 294 | |||
| 295 | ~CBurnBitsCallback() | ||
| 296 | { | ||
| 297 | m_pCallback = NULL; | ||
| 298 | ReleaseHandle(m_hComplete); | ||
| 299 | ::DeleteCriticalSection(&m_cs); | ||
| 300 | } | ||
| 301 | |||
| 302 | private: | ||
| 303 | long m_cReferences; | ||
| 304 | CRITICAL_SECTION m_cs; | ||
| 305 | BG_ERROR_CONTEXT m_contextError; | ||
| 306 | HRESULT m_hrError; | ||
| 307 | |||
| 308 | HANDLE m_hComplete; | ||
| 309 | DOWNLOAD_CACHE_CALLBACK* m_pCallback; | ||
| 310 | }; | ||
| 311 | |||
| 312 | |||
| 313 | extern "C" HRESULT BitsDownloadUrl( | ||
| 314 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 315 | __in DOWNLOAD_SOURCE* pDownloadSource, | ||
| 316 | __in_z LPCWSTR wzDestinationPath | ||
| 317 | ) | ||
| 318 | { | ||
| 319 | HRESULT hr = S_OK; | ||
| 320 | LPWSTR sczDownloadUrl = NULL; | ||
| 321 | CBurnBitsCallback* pBitsCallback = NULL; | ||
| 322 | IBackgroundCopyJob* pJob = NULL; | ||
| 323 | BOOL fRetry = FALSE; | ||
| 324 | BG_ERROR_CONTEXT contextError = BG_ERROR_CONTEXT_NONE; | ||
| 325 | |||
| 326 | // If the URL isn't at least 8 characters long (e.g.: "bits://X") then it | ||
| 327 | // isn't going to do us any good. | ||
| 328 | if (8 > lstrlenW(pDownloadSource->sczUrl)) | ||
| 329 | { | ||
| 330 | hr = E_INVALIDARG; | ||
| 331 | ExitOnRootFailure(hr, "Invalid BITS engine URL: %ls", pDownloadSource->sczUrl); | ||
| 332 | } | ||
| 333 | |||
| 334 | // Fix the URL to be "http" instead of "bits". | ||
| 335 | hr = StrAllocString(&sczDownloadUrl, pDownloadSource->sczUrl, 0); | ||
| 336 | ExitOnFailure(hr, "Failed to copy download URL."); | ||
| 337 | |||
| 338 | sczDownloadUrl[0] = L'h'; | ||
| 339 | sczDownloadUrl[1] = L't'; | ||
| 340 | sczDownloadUrl[2] = L't'; | ||
| 341 | sczDownloadUrl[3] = L'p'; | ||
| 342 | |||
| 343 | // Create and configure the BITS job. | ||
| 344 | hr = CreateJob(&pJob); | ||
| 345 | ExitOnFailure(hr, "Failed to create BITS job."); | ||
| 346 | |||
| 347 | hr = SetCredentials(pJob, pDownloadSource->sczUser, pDownloadSource->sczPassword); | ||
| 348 | ExitOnFailure(hr, "Failed to set credentials for BITS job."); | ||
| 349 | |||
| 350 | hr = pJob->AddFile(sczDownloadUrl, wzDestinationPath); | ||
| 351 | ExitOnFailure(hr, "Failed to add file to BITS job."); | ||
| 352 | |||
| 353 | // Set the callback into the BITs job. | ||
| 354 | pBitsCallback = new CBurnBitsCallback(pCallback, &hr); | ||
| 355 | ExitOnNull(pBitsCallback, hr, E_OUTOFMEMORY, "Failed to create BITS job callback."); | ||
| 356 | ExitOnFailure(hr, "Failed to initialize BITS job callback."); | ||
| 357 | |||
| 358 | hr = pJob->SetNotifyInterface(pBitsCallback); | ||
| 359 | ExitOnFailure(hr, "Failed to set callback interface for BITS job."); | ||
| 360 | |||
| 361 | // Go into our retry download loop. | ||
| 362 | do | ||
| 363 | { | ||
| 364 | fRetry = FALSE; | ||
| 365 | |||
| 366 | pBitsCallback->Reset(); // ensure we are ready for the download to start (again?). | ||
| 367 | |||
| 368 | hr = pJob->Resume(); | ||
| 369 | ExitOnFailure(hr, "Falied to start BITS job."); | ||
| 370 | |||
| 371 | hr = pBitsCallback->WaitForCompletion(pJob); | ||
| 372 | ExitOnFailure(hr, "Failed while waiting for BITS download."); | ||
| 373 | |||
| 374 | // See if there are any errors. | ||
| 375 | pBitsCallback->GetError(&hr, &contextError); | ||
| 376 | if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr) | ||
| 377 | { | ||
| 378 | ExitFunction(); | ||
| 379 | } | ||
| 380 | else if (FAILED(hr)) | ||
| 381 | { | ||
| 382 | SendError(pCallback, pJob, hr, contextError, &fRetry); | ||
| 383 | } | ||
| 384 | } while (fRetry); | ||
| 385 | ExitOnFailure(hr, "Failed to download BITS job."); | ||
| 386 | |||
| 387 | // After all that, we should have the file downloaded so complete the job to get | ||
| 388 | // the file copied to the destination. | ||
| 389 | hr = pJob->Complete(); | ||
| 390 | ExitOnFailure(hr, "Failed to complete BITS job."); | ||
| 391 | |||
| 392 | LExit: | ||
| 393 | if (pJob) | ||
| 394 | { | ||
| 395 | pJob->SetNotifyInterface(NULL); | ||
| 396 | |||
| 397 | // If we failed, kill the job. | ||
| 398 | if (FAILED(hr)) | ||
| 399 | { | ||
| 400 | pJob->Cancel(); // TODO: should we cancel if we're going to retry the package? Probably the right thing to do. | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | ReleaseObject(pBitsCallback); | ||
| 405 | ReleaseObject(pJob); | ||
| 406 | ReleaseStr(sczDownloadUrl); | ||
| 407 | |||
| 408 | return hr; | ||
| 409 | } | ||
| 410 | |||
| 411 | static HRESULT CreateJob( | ||
| 412 | __out IBackgroundCopyJob** ppJob | ||
| 413 | ) | ||
| 414 | { | ||
| 415 | HRESULT hr = S_OK; | ||
| 416 | IBackgroundCopyManager* pBitsManager = NULL; | ||
| 417 | IBackgroundCopyJob* pJob = NULL; | ||
| 418 | GUID guidJob = { }; | ||
| 419 | |||
| 420 | hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_ALL, __uuidof(IBackgroundCopyManager), reinterpret_cast<LPVOID*>(&pBitsManager)); | ||
| 421 | ExitOnFailure(hr, "Failed to create IBackgroundCopyManager."); | ||
| 422 | |||
| 423 | hr = pBitsManager->CreateJob(L"WixBurn", BG_JOB_TYPE_DOWNLOAD, &guidJob, &pJob); | ||
| 424 | ExitOnFailure(hr, "Failed to create BITS job."); | ||
| 425 | |||
| 426 | hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_MODIFICATION); | ||
| 427 | ExitOnFailure(hr, "Failed to set notification flags for BITS job."); | ||
| 428 | |||
| 429 | hr = pJob->SetNoProgressTimeout(BITSENGINE_NO_PROGRESS_TIMEOUT); // use 2 minutes since default is 14 days. | ||
| 430 | ExitOnFailure(hr, "Failed to set progress timeout."); | ||
| 431 | |||
| 432 | hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND); | ||
| 433 | ExitOnFailure(hr, "Failed to set BITS job to foreground."); | ||
| 434 | |||
| 435 | *ppJob = pJob; | ||
| 436 | pJob = NULL; | ||
| 437 | |||
| 438 | LExit: | ||
| 439 | ReleaseObject(pJob); | ||
| 440 | ReleaseObject(pBitsManager); | ||
| 441 | |||
| 442 | return hr; | ||
| 443 | } | ||
| 444 | |||
| 445 | static HRESULT SetCredentials( | ||
| 446 | __in IBackgroundCopyJob* pJob, | ||
| 447 | __in_z_opt LPCWSTR wzUser, | ||
| 448 | __in_z_opt LPCWSTR wzPassword | ||
| 449 | ) | ||
| 450 | { | ||
| 451 | HRESULT hr = S_OK; | ||
| 452 | IBackgroundCopyJob2* pJob2 = NULL; | ||
| 453 | BG_AUTH_CREDENTIALS ac = { }; | ||
| 454 | |||
| 455 | // If IBackgroundCopyJob2::SetCredentials() is supported, set the username/password. | ||
| 456 | hr = pJob->QueryInterface(IID_PPV_ARGS(&pJob2)); | ||
| 457 | if (SUCCEEDED(hr)) | ||
| 458 | { | ||
| 459 | ac.Target = BG_AUTH_TARGET_PROXY; | ||
| 460 | ac.Credentials.Basic.UserName = const_cast<LPWSTR>(wzUser); | ||
| 461 | ac.Credentials.Basic.Password = const_cast<LPWSTR>(wzPassword); | ||
| 462 | |||
| 463 | ac.Scheme = BG_AUTH_SCHEME_NTLM; | ||
| 464 | hr = pJob2->SetCredentials(&ac); | ||
| 465 | ExitOnFailure(hr, "Failed to set background copy NTLM credentials"); | ||
| 466 | |||
| 467 | ac.Scheme = BG_AUTH_SCHEME_NEGOTIATE; | ||
| 468 | hr = pJob2->SetCredentials(&ac); | ||
| 469 | ExitOnFailure(hr, "Failed to set background copy negotiate credentials"); | ||
| 470 | } | ||
| 471 | |||
| 472 | hr = S_OK; | ||
| 473 | |||
| 474 | LExit: | ||
| 475 | ReleaseObject(pJob2); | ||
| 476 | |||
| 477 | return hr; | ||
| 478 | } | ||
| 479 | |||
| 480 | static void SendError( | ||
| 481 | __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback, | ||
| 482 | __in IBackgroundCopyJob* pJob, | ||
| 483 | __in HRESULT hrError, | ||
| 484 | __in BG_ERROR_CONTEXT /*context*/, | ||
| 485 | __out_opt BOOL* pfRetry | ||
| 486 | ) | ||
| 487 | { | ||
| 488 | HRESULT hr = S_OK; | ||
| 489 | IBackgroundCopyError* pError = NULL; | ||
| 490 | LPWSTR pszErrorDescription = NULL; | ||
| 491 | |||
| 492 | hr = pJob->GetError(&pError); | ||
| 493 | if (SUCCEEDED(hr)) | ||
| 494 | { | ||
| 495 | pError->GetErrorDescription(LANGIDFROMLCID(::GetThreadLocale()), &pszErrorDescription); | ||
| 496 | } | ||
| 497 | |||
| 498 | CacheSendErrorCallback(pCacheCallback, hrError, pszErrorDescription, pfRetry); | ||
| 499 | |||
| 500 | if (pszErrorDescription) | ||
| 501 | { | ||
| 502 | ::CoTaskMemFree(pszErrorDescription); | ||
| 503 | } | ||
| 504 | ReleaseObject(pError); | ||
| 505 | } | ||
diff --git a/src/engine/bitsengine.h b/src/engine/bitsengine.h new file mode 100644 index 00000000..b1c1d805 --- /dev/null +++ b/src/engine/bitsengine.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | // structs | ||
| 10 | |||
| 11 | |||
| 12 | // functions | ||
| 13 | |||
| 14 | HRESULT BitsDownloadUrl( | ||
| 15 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 16 | __in DOWNLOAD_SOURCE* pDownloadSource, | ||
| 17 | __in LPCWSTR wzDestinationPath | ||
| 18 | ); | ||
| 19 | |||
| 20 | |||
| 21 | #ifdef __cplusplus | ||
| 22 | } | ||
| 23 | #endif | ||
diff --git a/src/engine/cabextract.cpp b/src/engine/cabextract.cpp new file mode 100644 index 00000000..04c2c6ec --- /dev/null +++ b/src/engine/cabextract.cpp | |||
| @@ -0,0 +1,974 @@ | |||
| 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 | |||
| 5 | #include <fdi.h> | ||
| 6 | |||
| 7 | #define ARRAY_GROWTH_SIZE 2 | ||
| 8 | |||
| 9 | const LPSTR INVALID_CAB_NAME = "<the>.cab"; | ||
| 10 | |||
| 11 | // structs | ||
| 12 | |||
| 13 | typedef struct _BURN_CAB_CONTEXT | ||
| 14 | { | ||
| 15 | HANDLE hFile; | ||
| 16 | DWORD64 qwOffset; | ||
| 17 | DWORD64 qwSize; | ||
| 18 | |||
| 19 | HANDLE hThread; | ||
| 20 | HANDLE hBeginOperationEvent; | ||
| 21 | HANDLE hOperationCompleteEvent; | ||
| 22 | |||
| 23 | BURN_CAB_OPERATION operation; | ||
| 24 | HRESULT hrError; | ||
| 25 | |||
| 26 | LPWSTR* psczStreamName; | ||
| 27 | LPCWSTR wzTargetFile; | ||
| 28 | HANDLE hTargetFile; | ||
| 29 | BYTE* pbTargetBuffer; | ||
| 30 | DWORD cbTargetBuffer; | ||
| 31 | DWORD iTargetBuffer; | ||
| 32 | } BURN_CAB_CONTEXT; | ||
| 33 | |||
| 34 | |||
| 35 | // internal function declarations | ||
| 36 | |||
| 37 | static HRESULT BeginAndWaitForOperation( | ||
| 38 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 39 | ); | ||
| 40 | static HRESULT WaitForOperation( | ||
| 41 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 42 | ); | ||
| 43 | static DWORD WINAPI ExtractThreadProc( | ||
| 44 | __in LPVOID lpThreadParameter | ||
| 45 | ); | ||
| 46 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
| 47 | __in FDINOTIFICATIONTYPE iNotification, | ||
| 48 | __inout FDINOTIFICATION *pFDINotify | ||
| 49 | ); | ||
| 50 | static INT_PTR CopyFileCallback( | ||
| 51 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 52 | __inout FDINOTIFICATION *pFDINotify | ||
| 53 | ); | ||
| 54 | static INT_PTR CloseFileInfoCallback( | ||
| 55 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 56 | __inout FDINOTIFICATION *pFDINotify | ||
| 57 | ); | ||
| 58 | static LPVOID DIAMONDAPI CabAlloc( | ||
| 59 | __in DWORD dwSize | ||
| 60 | ); | ||
| 61 | static void DIAMONDAPI CabFree( | ||
| 62 | __in LPVOID pvData | ||
| 63 | ); | ||
| 64 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
| 65 | __in char FAR *pszFile, | ||
| 66 | __in int /* oflag */, | ||
| 67 | __in int /* pmode */ | ||
| 68 | ); | ||
| 69 | static UINT FAR DIAMONDAPI CabRead( | ||
| 70 | __in INT_PTR hf, | ||
| 71 | __out void FAR *pv, | ||
| 72 | __in UINT cb | ||
| 73 | ); | ||
| 74 | static UINT FAR DIAMONDAPI CabWrite( | ||
| 75 | __in INT_PTR hf, | ||
| 76 | __in void FAR *pv, | ||
| 77 | __in UINT cb | ||
| 78 | ); | ||
| 79 | static long FAR DIAMONDAPI CabSeek( | ||
| 80 | __in INT_PTR hf, | ||
| 81 | __in long dist, | ||
| 82 | __in int seektype | ||
| 83 | ); | ||
| 84 | static int FAR DIAMONDAPI CabClose( | ||
| 85 | __in INT_PTR hf | ||
| 86 | ); | ||
| 87 | static HRESULT AddVirtualFilePointer( | ||
| 88 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 89 | __in HANDLE hFile, | ||
| 90 | __in LONGLONG llInitialFilePointer | ||
| 91 | ); | ||
| 92 | static HRESULT ReadIfVirtualFilePointer( | ||
| 93 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 94 | __in HANDLE hFile, | ||
| 95 | __in DWORD cbRead | ||
| 96 | ); | ||
| 97 | static BOOL SetIfVirtualFilePointer( | ||
| 98 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 99 | __in HANDLE hFile, | ||
| 100 | __in LONGLONG llDistance, | ||
| 101 | __out LONGLONG* pllNewPostion, | ||
| 102 | __in DWORD dwSeekType | ||
| 103 | ); | ||
| 104 | static HRESULT CloseIfVirturalFilePointer( | ||
| 105 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 106 | __in HANDLE hFile | ||
| 107 | ); | ||
| 108 | static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( | ||
| 109 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 110 | __in HANDLE hFile | ||
| 111 | ); | ||
| 112 | |||
| 113 | |||
| 114 | // internal variables | ||
| 115 | |||
| 116 | __declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; | ||
| 117 | |||
| 118 | |||
| 119 | // function definitions | ||
| 120 | |||
| 121 | extern "C" void CabExtractInitialize() | ||
| 122 | { | ||
| 123 | } | ||
| 124 | |||
| 125 | extern "C" HRESULT CabExtractOpen( | ||
| 126 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 127 | __in LPCWSTR wzFilePath | ||
| 128 | ) | ||
| 129 | { | ||
| 130 | HRESULT hr = S_OK; | ||
| 131 | |||
| 132 | // initialize context | ||
| 133 | pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; | ||
| 134 | |||
| 135 | hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); | ||
| 136 | ExitOnFailure(hr, "Failed to copy file name."); | ||
| 137 | |||
| 138 | // create events | ||
| 139 | pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 140 | ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); | ||
| 141 | |||
| 142 | pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 143 | ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); | ||
| 144 | |||
| 145 | // create extraction thread | ||
| 146 | pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); | ||
| 147 | ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); | ||
| 148 | |||
| 149 | // wait for operation to complete | ||
| 150 | hr = WaitForOperation(pContext); | ||
| 151 | ExitOnFailure(hr, "Failed to wait for operation complete."); | ||
| 152 | |||
| 153 | LExit: | ||
| 154 | return hr; | ||
| 155 | } | ||
| 156 | |||
| 157 | extern "C" HRESULT CabExtractNextStream( | ||
| 158 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 159 | __inout_z LPWSTR* psczStreamName | ||
| 160 | ) | ||
| 161 | { | ||
| 162 | HRESULT hr = S_OK; | ||
| 163 | |||
| 164 | // set operation to move to next stream | ||
| 165 | pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; | ||
| 166 | pContext->Cabinet.psczStreamName = psczStreamName; | ||
| 167 | |||
| 168 | // begin operation and wait | ||
| 169 | hr = BeginAndWaitForOperation(pContext); | ||
| 170 | if (E_ABORT != hr && E_NOMOREITEMS != hr) | ||
| 171 | { | ||
| 172 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
| 173 | } | ||
| 174 | |||
| 175 | LExit: | ||
| 176 | return hr; | ||
| 177 | } | ||
| 178 | |||
| 179 | extern "C" HRESULT CabExtractStreamToFile( | ||
| 180 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 181 | __in_z LPCWSTR wzFileName | ||
| 182 | ) | ||
| 183 | { | ||
| 184 | HRESULT hr = S_OK; | ||
| 185 | |||
| 186 | // set operation to move to next stream | ||
| 187 | pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; | ||
| 188 | pContext->Cabinet.wzTargetFile = wzFileName; | ||
| 189 | |||
| 190 | // begin operation and wait | ||
| 191 | hr = BeginAndWaitForOperation(pContext); | ||
| 192 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
| 193 | |||
| 194 | // clear file name | ||
| 195 | pContext->Cabinet.wzTargetFile = NULL; | ||
| 196 | |||
| 197 | LExit: | ||
| 198 | return hr; | ||
| 199 | } | ||
| 200 | |||
| 201 | extern "C" HRESULT CabExtractStreamToBuffer( | ||
| 202 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 203 | __out BYTE** ppbBuffer, | ||
| 204 | __out SIZE_T* pcbBuffer | ||
| 205 | ) | ||
| 206 | { | ||
| 207 | HRESULT hr = S_OK; | ||
| 208 | |||
| 209 | // set operation to move to next stream | ||
| 210 | pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; | ||
| 211 | |||
| 212 | // begin operation and wait | ||
| 213 | hr = BeginAndWaitForOperation(pContext); | ||
| 214 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
| 215 | |||
| 216 | // return values | ||
| 217 | *ppbBuffer = pContext->Cabinet.pbTargetBuffer; | ||
| 218 | *pcbBuffer = pContext->Cabinet.cbTargetBuffer; | ||
| 219 | |||
| 220 | // clear buffer variables | ||
| 221 | pContext->Cabinet.pbTargetBuffer = NULL; | ||
| 222 | pContext->Cabinet.cbTargetBuffer = 0; | ||
| 223 | pContext->Cabinet.iTargetBuffer = 0; | ||
| 224 | |||
| 225 | LExit: | ||
| 226 | return hr; | ||
| 227 | } | ||
| 228 | |||
| 229 | extern "C" HRESULT CabExtractSkipStream( | ||
| 230 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 231 | ) | ||
| 232 | { | ||
| 233 | HRESULT hr = S_OK; | ||
| 234 | |||
| 235 | // set operation to move to next stream | ||
| 236 | pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; | ||
| 237 | |||
| 238 | // begin operation and wait | ||
| 239 | hr = BeginAndWaitForOperation(pContext); | ||
| 240 | ExitOnFailure(hr, "Failed to begin and wait for operation."); | ||
| 241 | |||
| 242 | LExit: | ||
| 243 | return hr; | ||
| 244 | } | ||
| 245 | |||
| 246 | extern "C" HRESULT CabExtractClose( | ||
| 247 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 248 | ) | ||
| 249 | { | ||
| 250 | HRESULT hr = S_OK; | ||
| 251 | |||
| 252 | // terminate worker thread | ||
| 253 | if (pContext->Cabinet.hThread) | ||
| 254 | { | ||
| 255 | // set operation to move to close | ||
| 256 | pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; | ||
| 257 | |||
| 258 | // set begin operation event | ||
| 259 | if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
| 260 | { | ||
| 261 | ExitWithLastError(hr, "Failed to set begin operation event."); | ||
| 262 | } | ||
| 263 | |||
| 264 | // wait for thread to terminate | ||
| 265 | if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE)) | ||
| 266 | { | ||
| 267 | ExitWithLastError(hr, "Failed to wait for thread to terminate."); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | LExit: | ||
| 272 | ReleaseHandle(pContext->Cabinet.hThread); | ||
| 273 | ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); | ||
| 274 | ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); | ||
| 275 | ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); | ||
| 276 | ReleaseStr(pContext->Cabinet.sczFile); | ||
| 277 | |||
| 278 | return hr; | ||
| 279 | } | ||
| 280 | |||
| 281 | |||
| 282 | // internal helper functions | ||
| 283 | |||
| 284 | static HRESULT BeginAndWaitForOperation( | ||
| 285 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 286 | ) | ||
| 287 | { | ||
| 288 | HRESULT hr = S_OK; | ||
| 289 | |||
| 290 | // set begin operation event | ||
| 291 | if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
| 292 | { | ||
| 293 | ExitWithLastError(hr, "Failed to set begin operation event."); | ||
| 294 | } | ||
| 295 | |||
| 296 | // wait for operation to complete | ||
| 297 | hr = WaitForOperation(pContext); | ||
| 298 | |||
| 299 | LExit: | ||
| 300 | return hr; | ||
| 301 | } | ||
| 302 | |||
| 303 | static HRESULT WaitForOperation( | ||
| 304 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 305 | ) | ||
| 306 | { | ||
| 307 | HRESULT hr = S_OK; | ||
| 308 | HANDLE rghWait[2] = { }; | ||
| 309 | |||
| 310 | // wait for operation complete event | ||
| 311 | rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; | ||
| 312 | rghWait[1] = pContext->Cabinet.hThread; | ||
| 313 | switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE)) | ||
| 314 | { | ||
| 315 | case WAIT_OBJECT_0: | ||
| 316 | if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
| 317 | { | ||
| 318 | ExitWithLastError(hr, "Failed to reset operation complete event."); | ||
| 319 | } | ||
| 320 | break; | ||
| 321 | |||
| 322 | case WAIT_OBJECT_0 + 1: | ||
| 323 | if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) | ||
| 324 | { | ||
| 325 | ExitWithLastError(hr, "Failed to get extraction thread exit code."); | ||
| 326 | } | ||
| 327 | ExitFunction(); | ||
| 328 | |||
| 329 | case WAIT_FAILED: __fallthrough; | ||
| 330 | default: | ||
| 331 | ExitWithLastError(hr, "Failed to wait for operation complete event."); | ||
| 332 | } | ||
| 333 | |||
| 334 | // clear operation | ||
| 335 | pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; | ||
| 336 | |||
| 337 | LExit: | ||
| 338 | return hr; | ||
| 339 | } | ||
| 340 | |||
| 341 | static DWORD WINAPI ExtractThreadProc( | ||
| 342 | __in LPVOID lpThreadParameter | ||
| 343 | ) | ||
| 344 | { | ||
| 345 | HRESULT hr = S_OK; | ||
| 346 | BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; | ||
| 347 | BOOL fComInitialized = FALSE; | ||
| 348 | HFDI hfdi = NULL; | ||
| 349 | ERF erf = { }; | ||
| 350 | |||
| 351 | // initialize COM | ||
| 352 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 353 | ExitOnFailure(hr, "Failed to initialize COM."); | ||
| 354 | fComInitialized = TRUE; | ||
| 355 | |||
| 356 | // save context in TLS storage | ||
| 357 | vpContext = pContext; | ||
| 358 | |||
| 359 | // create FDI context | ||
| 360 | hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); | ||
| 361 | ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); | ||
| 362 | |||
| 363 | // begin CAB extraction | ||
| 364 | if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) | ||
| 365 | { | ||
| 366 | hr = pContext->Cabinet.hrError; | ||
| 367 | if (E_ABORT == hr || E_NOMOREITEMS == hr) | ||
| 368 | { | ||
| 369 | ExitFunction(); | ||
| 370 | } | ||
| 371 | else if (SUCCEEDED(hr)) | ||
| 372 | { | ||
| 373 | if (ERROR_SUCCESS != erf.erfType) | ||
| 374 | { | ||
| 375 | hr = HRESULT_FROM_WIN32(erf.erfType); | ||
| 376 | } | ||
| 377 | else | ||
| 378 | { | ||
| 379 | switch (erf.erfOper) | ||
| 380 | { | ||
| 381 | case FDIERROR_NONE: | ||
| 382 | hr = E_UNEXPECTED; | ||
| 383 | break; | ||
| 384 | case FDIERROR_CABINET_NOT_FOUND: | ||
| 385 | hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | ||
| 386 | break; | ||
| 387 | case FDIERROR_NOT_A_CABINET: | ||
| 388 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); | ||
| 389 | break; | ||
| 390 | case FDIERROR_UNKNOWN_CABINET_VERSION: | ||
| 391 | hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); | ||
| 392 | break; | ||
| 393 | case FDIERROR_CORRUPT_CABINET: | ||
| 394 | hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); | ||
| 395 | break; | ||
| 396 | case FDIERROR_ALLOC_FAIL: | ||
| 397 | hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); | ||
| 398 | break; | ||
| 399 | case FDIERROR_BAD_COMPR_TYPE: | ||
| 400 | hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); | ||
| 401 | break; | ||
| 402 | case FDIERROR_MDI_FAIL: | ||
| 403 | hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); | ||
| 404 | break; | ||
| 405 | case FDIERROR_TARGET_FILE: | ||
| 406 | hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); | ||
| 407 | break; | ||
| 408 | case FDIERROR_RESERVE_MISMATCH: | ||
| 409 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 410 | break; | ||
| 411 | case FDIERROR_WRONG_CABINET: | ||
| 412 | hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); | ||
| 413 | break; | ||
| 414 | case FDIERROR_USER_ABORT: | ||
| 415 | hr = E_ABORT; | ||
| 416 | break; | ||
| 417 | default: | ||
| 418 | hr = E_FAIL; | ||
| 419 | break; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
| 423 | ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); | ||
| 424 | } | ||
| 425 | |||
| 426 | // set operation complete event | ||
| 427 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
| 428 | { | ||
| 429 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
| 430 | } | ||
| 431 | |||
| 432 | // wait for begin operation event | ||
| 433 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
| 434 | { | ||
| 435 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
| 436 | } | ||
| 437 | |||
| 438 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
| 439 | { | ||
| 440 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
| 441 | } | ||
| 442 | |||
| 443 | // read operation | ||
| 444 | switch (pContext->Cabinet.operation) | ||
| 445 | { | ||
| 446 | case BURN_CAB_OPERATION_NEXT_STREAM: | ||
| 447 | ExitFunction1(hr = E_NOMOREITEMS); | ||
| 448 | break; | ||
| 449 | |||
| 450 | case BURN_CAB_OPERATION_CLOSE: | ||
| 451 | ExitFunction1(hr = S_OK); | ||
| 452 | |||
| 453 | default: | ||
| 454 | hr = E_INVALIDSTATE; | ||
| 455 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
| 456 | } | ||
| 457 | |||
| 458 | LExit: | ||
| 459 | if (hfdi) | ||
| 460 | { | ||
| 461 | ::FDIDestroy(hfdi); | ||
| 462 | } | ||
| 463 | if (fComInitialized) | ||
| 464 | { | ||
| 465 | ::CoUninitialize(); | ||
| 466 | } | ||
| 467 | |||
| 468 | return (DWORD)hr; | ||
| 469 | } | ||
| 470 | |||
| 471 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
| 472 | __in FDINOTIFICATIONTYPE iNotification, | ||
| 473 | __inout FDINOTIFICATION *pFDINotify | ||
| 474 | ) | ||
| 475 | { | ||
| 476 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 477 | INT_PTR ipResult = 0; // result to return on success | ||
| 478 | |||
| 479 | switch (iNotification) | ||
| 480 | { | ||
| 481 | case fdintCOPY_FILE: | ||
| 482 | ipResult = CopyFileCallback(pContext, pFDINotify); | ||
| 483 | break; | ||
| 484 | |||
| 485 | case fdintCLOSE_FILE_INFO: // resource extraction complete | ||
| 486 | ipResult = CloseFileInfoCallback(pContext, pFDINotify); | ||
| 487 | break; | ||
| 488 | |||
| 489 | case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages | ||
| 490 | case fdintNEXT_CABINET: __fallthrough; | ||
| 491 | case fdintENUMERATE: __fallthrough; | ||
| 492 | case fdintCABINET_INFO: | ||
| 493 | break; | ||
| 494 | |||
| 495 | default: | ||
| 496 | AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); | ||
| 497 | }; | ||
| 498 | |||
| 499 | //LExit: | ||
| 500 | return ipResult; | ||
| 501 | } | ||
| 502 | |||
| 503 | static INT_PTR CopyFileCallback( | ||
| 504 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 505 | __inout FDINOTIFICATION* pFDINotify | ||
| 506 | ) | ||
| 507 | { | ||
| 508 | HRESULT hr = S_OK; | ||
| 509 | INT_PTR ipResult = 1; // result to return on success | ||
| 510 | LPWSTR pwzPath = NULL; | ||
| 511 | LARGE_INTEGER li = { }; | ||
| 512 | |||
| 513 | // set operation complete event | ||
| 514 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
| 515 | { | ||
| 516 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
| 517 | } | ||
| 518 | |||
| 519 | // wait for begin operation event | ||
| 520 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
| 521 | { | ||
| 522 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
| 523 | } | ||
| 524 | |||
| 525 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
| 526 | { | ||
| 527 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
| 528 | } | ||
| 529 | |||
| 530 | // read operation | ||
| 531 | switch (pContext->Cabinet.operation) | ||
| 532 | { | ||
| 533 | case BURN_CAB_OPERATION_NEXT_STREAM: | ||
| 534 | break; | ||
| 535 | |||
| 536 | case BURN_CAB_OPERATION_CLOSE: | ||
| 537 | ExitFunction1(hr = E_ABORT); | ||
| 538 | |||
| 539 | default: | ||
| 540 | hr = E_INVALIDSTATE; | ||
| 541 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
| 542 | } | ||
| 543 | |||
| 544 | // copy stream name | ||
| 545 | hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); | ||
| 546 | ExitOnFailure(hr, "Failed to copy stream name: %ls", pFDINotify->psz1); | ||
| 547 | |||
| 548 | // set operation complete event | ||
| 549 | if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) | ||
| 550 | { | ||
| 551 | ExitWithLastError(hr, "Failed to set operation complete event."); | ||
| 552 | } | ||
| 553 | |||
| 554 | // wait for begin operation event | ||
| 555 | if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE)) | ||
| 556 | { | ||
| 557 | ExitWithLastError(hr, "Failed to wait for begin operation event."); | ||
| 558 | } | ||
| 559 | |||
| 560 | if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) | ||
| 561 | { | ||
| 562 | ExitWithLastError(hr, "Failed to reset begin operation event."); | ||
| 563 | } | ||
| 564 | |||
| 565 | // read operation | ||
| 566 | switch (pContext->Cabinet.operation) | ||
| 567 | { | ||
| 568 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
| 569 | // create file | ||
| 570 | pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
| 571 | if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) | ||
| 572 | { | ||
| 573 | ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); | ||
| 574 | } | ||
| 575 | |||
| 576 | // set file size | ||
| 577 | li.QuadPart = pFDINotify->cb; | ||
| 578 | if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) | ||
| 579 | { | ||
| 580 | ExitWithLastError(hr, "Failed to set file pointer to end of file."); | ||
| 581 | } | ||
| 582 | |||
| 583 | if (!::SetEndOfFile(pContext->Cabinet.hTargetFile)) | ||
| 584 | { | ||
| 585 | ExitWithLastError(hr, "Failed to set end of file."); | ||
| 586 | } | ||
| 587 | |||
| 588 | li.QuadPart = 0; | ||
| 589 | if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN)) | ||
| 590 | { | ||
| 591 | ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); | ||
| 592 | } | ||
| 593 | |||
| 594 | break; | ||
| 595 | |||
| 596 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
| 597 | // allocate buffer for stream | ||
| 598 | pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); | ||
| 599 | ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); | ||
| 600 | |||
| 601 | // set buffer size and write position | ||
| 602 | pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; | ||
| 603 | pContext->Cabinet.iTargetBuffer = 0; | ||
| 604 | |||
| 605 | break; | ||
| 606 | |||
| 607 | case BURN_CAB_OPERATION_SKIP_STREAM: | ||
| 608 | ipResult = 0; | ||
| 609 | break; | ||
| 610 | |||
| 611 | case BURN_CAB_OPERATION_CLOSE: | ||
| 612 | ExitFunction1(hr = E_ABORT); | ||
| 613 | |||
| 614 | default: | ||
| 615 | hr = E_INVALIDSTATE; | ||
| 616 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
| 617 | } | ||
| 618 | |||
| 619 | LExit: | ||
| 620 | ReleaseStr(pwzPath); | ||
| 621 | |||
| 622 | pContext->Cabinet.hrError = hr; | ||
| 623 | return SUCCEEDED(hr) ? ipResult : -1; | ||
| 624 | } | ||
| 625 | |||
| 626 | static INT_PTR CloseFileInfoCallback( | ||
| 627 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 628 | __inout FDINOTIFICATION *pFDINotify | ||
| 629 | ) | ||
| 630 | { | ||
| 631 | HRESULT hr = S_OK; | ||
| 632 | INT_PTR ipResult = 1; // result to return on success | ||
| 633 | FILETIME ftLocal = { }; | ||
| 634 | FILETIME ft = { }; | ||
| 635 | |||
| 636 | // read operation | ||
| 637 | switch (pContext->Cabinet.operation) | ||
| 638 | { | ||
| 639 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
| 640 | // Make a best effort to set the time on the new file before | ||
| 641 | // we close it. | ||
| 642 | if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) | ||
| 643 | { | ||
| 644 | if (::LocalFileTimeToFileTime(&ftLocal, &ft)) | ||
| 645 | { | ||
| 646 | ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft); | ||
| 647 | } | ||
| 648 | } | ||
| 649 | |||
| 650 | // close file | ||
| 651 | ReleaseFile(pContext->Cabinet.hTargetFile); | ||
| 652 | break; | ||
| 653 | |||
| 654 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
| 655 | break; | ||
| 656 | |||
| 657 | case BURN_CAB_OPERATION_CLOSE: | ||
| 658 | ExitFunction1(hr = E_ABORT); | ||
| 659 | |||
| 660 | default: | ||
| 661 | hr = E_INVALIDSTATE; | ||
| 662 | ExitOnRootFailure(hr, "Invalid operation for this state."); | ||
| 663 | } | ||
| 664 | |||
| 665 | //if (pContext->pfnProgress) | ||
| 666 | //{ | ||
| 667 | // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); | ||
| 668 | // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); | ||
| 669 | // if (SUCCEEDED(hr)) | ||
| 670 | // { | ||
| 671 | // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); | ||
| 672 | // if (S_OK != hr) | ||
| 673 | // { | ||
| 674 | // pContext->hrError = hr; | ||
| 675 | // ExitFunction(); | ||
| 676 | // } | ||
| 677 | // } | ||
| 678 | //} | ||
| 679 | |||
| 680 | LExit: | ||
| 681 | pContext->Cabinet.hrError = hr; | ||
| 682 | return SUCCEEDED(hr) ? ipResult : -1; | ||
| 683 | } | ||
| 684 | |||
| 685 | static LPVOID DIAMONDAPI CabAlloc( | ||
| 686 | __in DWORD dwSize | ||
| 687 | ) | ||
| 688 | { | ||
| 689 | return MemAlloc(dwSize, FALSE); | ||
| 690 | } | ||
| 691 | |||
| 692 | static void DIAMONDAPI CabFree( | ||
| 693 | __in LPVOID pvData | ||
| 694 | ) | ||
| 695 | { | ||
| 696 | MemFree(pvData); | ||
| 697 | } | ||
| 698 | |||
| 699 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
| 700 | __in char FAR * pszFile, | ||
| 701 | __in int /* oflag */, | ||
| 702 | __in int /* pmode */ | ||
| 703 | ) | ||
| 704 | { | ||
| 705 | HRESULT hr = S_OK; | ||
| 706 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 707 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
| 708 | |||
| 709 | // If this is the invalid cab name, use our file handle. | ||
| 710 | if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) | ||
| 711 | { | ||
| 712 | if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
| 713 | { | ||
| 714 | ExitWithLastError(hr, "Failed to duplicate handle to cab container."); | ||
| 715 | } | ||
| 716 | |||
| 717 | // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset | ||
| 718 | // to start. | ||
| 719 | hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); | ||
| 720 | ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); | ||
| 721 | } | ||
| 722 | else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. | ||
| 723 | { | ||
| 724 | hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 725 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); | ||
| 726 | } | ||
| 727 | |||
| 728 | LExit: | ||
| 729 | pContext->Cabinet.hrError = hr; | ||
| 730 | return FAILED(hr) ? -1 : (INT_PTR)hFile; | ||
| 731 | } | ||
| 732 | |||
| 733 | static UINT FAR DIAMONDAPI CabRead( | ||
| 734 | __in INT_PTR hf, | ||
| 735 | __out void FAR *pv, | ||
| 736 | __in UINT cb | ||
| 737 | ) | ||
| 738 | { | ||
| 739 | HRESULT hr = S_OK; | ||
| 740 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 741 | HANDLE hFile = (HANDLE)hf; | ||
| 742 | DWORD cbRead = 0; | ||
| 743 | |||
| 744 | ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); | ||
| 745 | |||
| 746 | if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) | ||
| 747 | { | ||
| 748 | ExitWithLastError(hr, "Failed to read during cabinet extraction."); | ||
| 749 | } | ||
| 750 | |||
| 751 | LExit: | ||
| 752 | pContext->Cabinet.hrError = hr; | ||
| 753 | return FAILED(hr) ? -1 : cbRead; | ||
| 754 | } | ||
| 755 | |||
| 756 | static UINT FAR DIAMONDAPI CabWrite( | ||
| 757 | __in INT_PTR /* hf */, | ||
| 758 | __in void FAR *pv, | ||
| 759 | __in UINT cb | ||
| 760 | ) | ||
| 761 | { | ||
| 762 | HRESULT hr = S_OK; | ||
| 763 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 764 | DWORD cbWrite = 0; | ||
| 765 | |||
| 766 | switch (pContext->Cabinet.operation) | ||
| 767 | { | ||
| 768 | case BURN_CAB_OPERATION_STREAM_TO_FILE: | ||
| 769 | // write file | ||
| 770 | if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) | ||
| 771 | { | ||
| 772 | ExitWithLastError(hr, "Failed to write during cabinet extraction."); | ||
| 773 | } | ||
| 774 | break; | ||
| 775 | |||
| 776 | case BURN_CAB_OPERATION_STREAM_TO_BUFFER: | ||
| 777 | // copy to target buffer | ||
| 778 | memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); | ||
| 779 | pContext->Cabinet.iTargetBuffer += cb; | ||
| 780 | |||
| 781 | cbWrite = cb; | ||
| 782 | break; | ||
| 783 | |||
| 784 | default: | ||
| 785 | hr = E_INVALIDSTATE; | ||
| 786 | ExitOnFailure(hr, "Unexpected call to CabWrite()."); | ||
| 787 | } | ||
| 788 | |||
| 789 | LExit: | ||
| 790 | pContext->Cabinet.hrError = hr; | ||
| 791 | return FAILED(hr) ? -1 : cbWrite; | ||
| 792 | } | ||
| 793 | |||
| 794 | static long FAR DIAMONDAPI CabSeek( | ||
| 795 | __in INT_PTR hf, | ||
| 796 | __in long dist, | ||
| 797 | __in int seektype | ||
| 798 | ) | ||
| 799 | { | ||
| 800 | HRESULT hr = S_OK; | ||
| 801 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 802 | HANDLE hFile = (HANDLE)hf; | ||
| 803 | LARGE_INTEGER liDistance = { }; | ||
| 804 | LARGE_INTEGER liNewPointer = { }; | ||
| 805 | DWORD dwSeekType = 0; | ||
| 806 | |||
| 807 | // We assume that CabSeek() will only be called to seek the | ||
| 808 | // cabinet itself so we have to offset the seek operations to | ||
| 809 | // where the internal cabinet starts. | ||
| 810 | switch (seektype) | ||
| 811 | { | ||
| 812 | case FILE_BEGIN: | ||
| 813 | liDistance.QuadPart = pContext->qwOffset + dist; | ||
| 814 | dwSeekType = FILE_BEGIN; | ||
| 815 | break; | ||
| 816 | |||
| 817 | case FILE_CURRENT: | ||
| 818 | liDistance.QuadPart = dist; | ||
| 819 | dwSeekType = FILE_CURRENT; | ||
| 820 | break; | ||
| 821 | |||
| 822 | case FILE_END: | ||
| 823 | liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; | ||
| 824 | dwSeekType = FILE_BEGIN; | ||
| 825 | break; | ||
| 826 | |||
| 827 | default: | ||
| 828 | hr = E_INVALIDARG; | ||
| 829 | ExitOnFailure(hr, "Invalid seek type.");; | ||
| 830 | } | ||
| 831 | |||
| 832 | if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) | ||
| 833 | { | ||
| 834 | // set file pointer | ||
| 835 | if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) | ||
| 836 | { | ||
| 837 | ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | liNewPointer.QuadPart -= pContext->qwOffset; | ||
| 842 | |||
| 843 | LExit: | ||
| 844 | pContext->Cabinet.hrError = hr; | ||
| 845 | return FAILED(hr) ? -1 : liNewPointer.LowPart; | ||
| 846 | } | ||
| 847 | |||
| 848 | static int FAR DIAMONDAPI CabClose( | ||
| 849 | __in INT_PTR hf | ||
| 850 | ) | ||
| 851 | { | ||
| 852 | BURN_CONTAINER_CONTEXT* pContext = vpContext; | ||
| 853 | HANDLE hFile = (HANDLE)hf; | ||
| 854 | |||
| 855 | CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); | ||
| 856 | ReleaseFileHandle(hFile); | ||
| 857 | |||
| 858 | return 0; | ||
| 859 | } | ||
| 860 | |||
| 861 | static HRESULT AddVirtualFilePointer( | ||
| 862 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 863 | __in HANDLE hFile, | ||
| 864 | __in LONGLONG llInitialFilePointer | ||
| 865 | ) | ||
| 866 | { | ||
| 867 | HRESULT hr = S_OK; | ||
| 868 | |||
| 869 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); | ||
| 870 | ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); | ||
| 871 | |||
| 872 | pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; | ||
| 873 | pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; | ||
| 874 | ++pCabinetContext->cVirtualFilePointers; | ||
| 875 | |||
| 876 | LExit: | ||
| 877 | return hr; | ||
| 878 | } | ||
| 879 | |||
| 880 | static HRESULT ReadIfVirtualFilePointer( | ||
| 881 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 882 | __in HANDLE hFile, | ||
| 883 | __in DWORD cbRead | ||
| 884 | ) | ||
| 885 | { | ||
| 886 | HRESULT hr = E_NOTFOUND; | ||
| 887 | |||
| 888 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
| 889 | if (pVfp) | ||
| 890 | { | ||
| 891 | // Set the file handle to the virtual file pointer. | ||
| 892 | if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) | ||
| 893 | { | ||
| 894 | ExitWithLastError(hr, "Failed to move to virtual file pointer."); | ||
| 895 | } | ||
| 896 | |||
| 897 | pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. | ||
| 898 | hr = S_OK; | ||
| 899 | } | ||
| 900 | |||
| 901 | LExit: | ||
| 902 | return hr; | ||
| 903 | } | ||
| 904 | |||
| 905 | static BOOL SetIfVirtualFilePointer( | ||
| 906 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 907 | __in HANDLE hFile, | ||
| 908 | __in LONGLONG llDistance, | ||
| 909 | __out LONGLONG* pllNewPostion, | ||
| 910 | __in DWORD dwSeekType | ||
| 911 | ) | ||
| 912 | { | ||
| 913 | BOOL fFound = FALSE; | ||
| 914 | |||
| 915 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
| 916 | if (pVfp) | ||
| 917 | { | ||
| 918 | switch (dwSeekType) | ||
| 919 | { | ||
| 920 | case FILE_BEGIN: | ||
| 921 | pVfp->liPosition.QuadPart = llDistance; | ||
| 922 | break; | ||
| 923 | |||
| 924 | case FILE_CURRENT: | ||
| 925 | pVfp->liPosition.QuadPart += llDistance; | ||
| 926 | break; | ||
| 927 | |||
| 928 | case FILE_END: __fallthrough; | ||
| 929 | default: | ||
| 930 | AssertSz(FALSE, "Unsupported seek type."); | ||
| 931 | break; | ||
| 932 | } | ||
| 933 | |||
| 934 | *pllNewPostion = pVfp->liPosition.QuadPart; | ||
| 935 | fFound = TRUE; | ||
| 936 | } | ||
| 937 | |||
| 938 | return fFound; | ||
| 939 | } | ||
| 940 | |||
| 941 | static HRESULT CloseIfVirturalFilePointer( | ||
| 942 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 943 | __in HANDLE hFile | ||
| 944 | ) | ||
| 945 | { | ||
| 946 | HRESULT hr = E_NOTFOUND; | ||
| 947 | |||
| 948 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); | ||
| 949 | if (pVfp) | ||
| 950 | { | ||
| 951 | pVfp->hFile = INVALID_HANDLE_VALUE; | ||
| 952 | pVfp->liPosition.QuadPart = 0; | ||
| 953 | hr = S_OK; | ||
| 954 | } | ||
| 955 | |||
| 956 | return hr; | ||
| 957 | } | ||
| 958 | |||
| 959 | static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( | ||
| 960 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
| 961 | __in HANDLE hFile | ||
| 962 | ) | ||
| 963 | { | ||
| 964 | for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) | ||
| 965 | { | ||
| 966 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; | ||
| 967 | if (pVfp->hFile == hFile) | ||
| 968 | { | ||
| 969 | return pVfp; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | |||
| 973 | return NULL; | ||
| 974 | } | ||
diff --git a/src/engine/cabextract.h b/src/engine/cabextract.h new file mode 100644 index 00000000..31667f2b --- /dev/null +++ b/src/engine/cabextract.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // function declarations | ||
| 11 | |||
| 12 | void CabExtractInitialize(); | ||
| 13 | HRESULT CabExtractOpen( | ||
| 14 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 15 | __in LPCWSTR wzFilePath | ||
| 16 | ); | ||
| 17 | HRESULT CabExtractNextStream( | ||
| 18 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 19 | __inout_z LPWSTR* psczStreamName | ||
| 20 | ); | ||
| 21 | HRESULT CabExtractStreamToFile( | ||
| 22 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 23 | __in_z LPCWSTR wzFileName | ||
| 24 | ); | ||
| 25 | HRESULT CabExtractStreamToBuffer( | ||
| 26 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 27 | __out BYTE** ppbBuffer, | ||
| 28 | __out SIZE_T* pcbBuffer | ||
| 29 | ); | ||
| 30 | HRESULT CabExtractSkipStream( | ||
| 31 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 32 | ); | ||
| 33 | HRESULT CabExtractClose( | ||
| 34 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 35 | ); | ||
| 36 | |||
| 37 | |||
| 38 | #if defined(__cplusplus) | ||
| 39 | } | ||
| 40 | #endif | ||
diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp new file mode 100644 index 00000000..9338426d --- /dev/null +++ b/src/engine/cache.cpp | |||
| @@ -0,0 +1,2026 @@ | |||
| 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 | |||
| 5 | static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; | ||
| 6 | static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; | ||
| 7 | static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; | ||
| 8 | static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; | ||
| 9 | static const DWORD FILE_OPERATION_RETRY_COUNT = 3; | ||
| 10 | static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; | ||
| 11 | |||
| 12 | static BOOL vfInitializedCache = FALSE; | ||
| 13 | static BOOL vfRunningFromCache = FALSE; | ||
| 14 | static LPWSTR vsczSourceProcessPath = NULL; | ||
| 15 | static LPWSTR vsczWorkingFolder = NULL; | ||
| 16 | static LPWSTR vsczDefaultUserPackageCache = NULL; | ||
| 17 | static LPWSTR vsczDefaultMachinePackageCache = NULL; | ||
| 18 | static LPWSTR vsczCurrentMachinePackageCache = NULL; | ||
| 19 | |||
| 20 | static HRESULT CalculateWorkingFolder( | ||
| 21 | __in_z LPCWSTR wzBundleId, | ||
| 22 | __deref_out_z LPWSTR* psczWorkingFolder | ||
| 23 | ); | ||
| 24 | static HRESULT GetLastUsedSourceFolder( | ||
| 25 | __in BURN_VARIABLES* pVariables, | ||
| 26 | __out_z LPWSTR* psczLastSource | ||
| 27 | ); | ||
| 28 | static HRESULT CreateCompletedPath( | ||
| 29 | __in BOOL fPerMachine, | ||
| 30 | __in LPCWSTR wzCacheId, | ||
| 31 | __out LPWSTR* psczCacheDirectory | ||
| 32 | ); | ||
| 33 | static HRESULT CreateUnverifiedPath( | ||
| 34 | __in BOOL fPerMachine, | ||
| 35 | __in_z LPCWSTR wzPayloadId, | ||
| 36 | __out_z LPWSTR* psczUnverifiedPayloadPath | ||
| 37 | ); | ||
| 38 | static HRESULT GetRootPath( | ||
| 39 | __in BOOL fPerMachine, | ||
| 40 | __in BOOL fAllowRedirect, | ||
| 41 | __deref_out_z LPWSTR* psczRootPath | ||
| 42 | ); | ||
| 43 | static HRESULT VerifyThenTransferContainer( | ||
| 44 | __in BURN_CONTAINER* pContainer, | ||
| 45 | __in_z LPCWSTR wzCachedPath, | ||
| 46 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
| 47 | __in BOOL fMove | ||
| 48 | ); | ||
| 49 | static HRESULT VerifyThenTransferPayload( | ||
| 50 | __in BURN_PAYLOAD* pPayload, | ||
| 51 | __in_z LPCWSTR wzCachedPath, | ||
| 52 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 53 | __in BOOL fMove | ||
| 54 | ); | ||
| 55 | static HRESULT TransferWorkingPathToUnverifiedPath( | ||
| 56 | __in_z LPCWSTR wzWorkingPath, | ||
| 57 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 58 | __in BOOL fMove | ||
| 59 | ); | ||
| 60 | static HRESULT VerifyFileAgainstPayload( | ||
| 61 | __in BURN_PAYLOAD* pPayload, | ||
| 62 | __in_z LPCWSTR wzVerifyPath | ||
| 63 | ); | ||
| 64 | static HRESULT ResetPathPermissions( | ||
| 65 | __in BOOL fPerMachine, | ||
| 66 | __in_z LPCWSTR wzPath | ||
| 67 | ); | ||
| 68 | static HRESULT SecurePath( | ||
| 69 | __in LPCWSTR wzPath | ||
| 70 | ); | ||
| 71 | static HRESULT CopyEngineToWorkingFolder( | ||
| 72 | __in_z LPCWSTR wzSourcePath, | ||
| 73 | __in_z LPCWSTR wzWorkingFolderName, | ||
| 74 | __in_z LPCWSTR wzExecutableName, | ||
| 75 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 76 | __in BURN_SECTION* pSection, | ||
| 77 | __deref_out_z_opt LPWSTR* psczEngineWorkingPath | ||
| 78 | ); | ||
| 79 | static HRESULT CopyEngineWithSignatureFixup( | ||
| 80 | __in HANDLE hEngineFile, | ||
| 81 | __in_z LPCWSTR wzEnginePath, | ||
| 82 | __in_z LPCWSTR wzTargetPath, | ||
| 83 | __in BURN_SECTION* pSection | ||
| 84 | ); | ||
| 85 | static HRESULT RemoveBundleOrPackage( | ||
| 86 | __in BOOL fBundle, | ||
| 87 | __in BOOL fPerMachine, | ||
| 88 | __in_z LPCWSTR wzBundleOrPackageId, | ||
| 89 | __in_z LPCWSTR wzCacheId | ||
| 90 | ); | ||
| 91 | static HRESULT VerifyHash( | ||
| 92 | __in BYTE* pbHash, | ||
| 93 | __in DWORD cbHash, | ||
| 94 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 95 | __in HANDLE hFile | ||
| 96 | ); | ||
| 97 | static HRESULT VerifyPayloadWithCatalog( | ||
| 98 | __in BURN_PAYLOAD* pPayload, | ||
| 99 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 100 | __in HANDLE hFile | ||
| 101 | ); | ||
| 102 | static HRESULT VerifyPayloadAgainstChain( | ||
| 103 | __in BURN_PAYLOAD* pPayload, | ||
| 104 | __in PCCERT_CHAIN_CONTEXT pChainContext | ||
| 105 | ); | ||
| 106 | |||
| 107 | |||
| 108 | extern "C" HRESULT CacheInitialize( | ||
| 109 | __in BURN_REGISTRATION* pRegistration, | ||
| 110 | __in BURN_VARIABLES* pVariables, | ||
| 111 | __in_z_opt LPCWSTR wzSourceProcessPath | ||
| 112 | ) | ||
| 113 | { | ||
| 114 | HRESULT hr = S_OK; | ||
| 115 | LPWSTR sczCurrentPath = NULL; | ||
| 116 | LPWSTR sczCompletedFolder = NULL; | ||
| 117 | LPWSTR sczCompletedPath = NULL; | ||
| 118 | LPWSTR sczOriginalSource = NULL; | ||
| 119 | LPWSTR sczOriginalSourceFolder = NULL; | ||
| 120 | int nCompare = 0; | ||
| 121 | |||
| 122 | if (!vfInitializedCache) | ||
| 123 | { | ||
| 124 | hr = PathForCurrentProcess(&sczCurrentPath, NULL); | ||
| 125 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 126 | |||
| 127 | // Determine if we are running from the package cache or not. | ||
| 128 | hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder); | ||
| 129 | ExitOnFailure(hr, "Failed to get completed path for bundle."); | ||
| 130 | |||
| 131 | hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath); | ||
| 132 | ExitOnFailure(hr, "Failed to combine working path with engine file name."); | ||
| 133 | |||
| 134 | hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare); | ||
| 135 | ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath); | ||
| 136 | |||
| 137 | vfRunningFromCache = (CSTR_EQUAL == nCompare); | ||
| 138 | |||
| 139 | // If a source process path was not provided (e.g. we are not being | ||
| 140 | // run in a clean room) then use the current process path as the | ||
| 141 | // source process path. | ||
| 142 | if (!wzSourceProcessPath) | ||
| 143 | { | ||
| 144 | wzSourceProcessPath = sczCurrentPath; | ||
| 145 | } | ||
| 146 | |||
| 147 | hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0); | ||
| 148 | ExitOnFailure(hr, "Failed to initialize cache source path."); | ||
| 149 | |||
| 150 | // If we're not running from the cache, ensure the original source is set. | ||
| 151 | if (!vfRunningFromCache) | ||
| 152 | { | ||
| 153 | // If the original source has not been set already then set it where the bundle is | ||
| 154 | // running from right now. This value will be persisted and we'll use it when launched | ||
| 155 | // from the clean room or package cache since none of our packages will be relative to | ||
| 156 | // those locations. | ||
| 157 | hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); | ||
| 158 | if (E_NOTFOUND == hr) | ||
| 159 | { | ||
| 160 | hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE); | ||
| 161 | ExitOnFailure(hr, "Failed to set original source variable."); | ||
| 162 | |||
| 163 | hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0); | ||
| 164 | ExitOnFailure(hr, "Failed to copy current path to original source."); | ||
| 165 | } | ||
| 166 | |||
| 167 | hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder); | ||
| 168 | if (E_NOTFOUND == hr) | ||
| 169 | { | ||
| 170 | hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder); | ||
| 171 | ExitOnFailure(hr, "Failed to get directory from original source path."); | ||
| 172 | |||
| 173 | hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE); | ||
| 174 | ExitOnFailure(hr, "Failed to set original source directory variable."); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | vfInitializedCache = TRUE; | ||
| 179 | } | ||
| 180 | |||
| 181 | LExit: | ||
| 182 | ReleaseStr(sczCurrentPath); | ||
| 183 | ReleaseStr(sczCompletedFolder); | ||
| 184 | ReleaseStr(sczCompletedPath); | ||
| 185 | ReleaseStr(sczOriginalSource); | ||
| 186 | ReleaseStr(sczOriginalSourceFolder); | ||
| 187 | |||
| 188 | return hr; | ||
| 189 | } | ||
| 190 | |||
| 191 | extern "C" HRESULT CacheEnsureWorkingFolder( | ||
| 192 | __in_z LPCWSTR wzBundleId, | ||
| 193 | __deref_out_z_opt LPWSTR* psczWorkingFolder | ||
| 194 | ) | ||
| 195 | { | ||
| 196 | HRESULT hr = S_OK; | ||
| 197 | LPWSTR sczWorkingFolder = NULL; | ||
| 198 | |||
| 199 | hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); | ||
| 200 | ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists."); | ||
| 201 | |||
| 202 | hr = DirEnsureExists(sczWorkingFolder, NULL); | ||
| 203 | ExitOnFailure(hr, "Failed create working folder."); | ||
| 204 | |||
| 205 | // Best effort to ensure our working folder is not encrypted. | ||
| 206 | ::DecryptFileW(sczWorkingFolder, 0); | ||
| 207 | |||
| 208 | if (psczWorkingFolder) | ||
| 209 | { | ||
| 210 | hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0); | ||
| 211 | ExitOnFailure(hr, "Failed to copy working folder."); | ||
| 212 | } | ||
| 213 | |||
| 214 | LExit: | ||
| 215 | ReleaseStr(sczWorkingFolder); | ||
| 216 | |||
| 217 | return hr; | ||
| 218 | } | ||
| 219 | |||
| 220 | extern "C" HRESULT CacheCalculateBundleWorkingPath( | ||
| 221 | __in_z LPCWSTR wzBundleId, | ||
| 222 | __in LPCWSTR wzExecutableName, | ||
| 223 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 224 | ) | ||
| 225 | { | ||
| 226 | Assert(vfInitializedCache); | ||
| 227 | |||
| 228 | HRESULT hr = S_OK; | ||
| 229 | LPWSTR sczWorkingFolder = NULL; | ||
| 230 | |||
| 231 | // If the bundle is running out of the package cache then we use that as the | ||
| 232 | // working folder since we feel safe in the package cache. | ||
| 233 | if (vfRunningFromCache) | ||
| 234 | { | ||
| 235 | hr = PathForCurrentProcess(psczWorkingPath, NULL); | ||
| 236 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 237 | } | ||
| 238 | else // Otherwise, use the real working folder. | ||
| 239 | { | ||
| 240 | hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); | ||
| 241 | ExitOnFailure(hr, "Failed to get working folder for bundle."); | ||
| 242 | |||
| 243 | hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName); | ||
| 244 | ExitOnFailure(hr, "Failed to calculate the bundle working path."); | ||
| 245 | } | ||
| 246 | |||
| 247 | LExit: | ||
| 248 | ReleaseStr(sczWorkingFolder); | ||
| 249 | |||
| 250 | return hr; | ||
| 251 | } | ||
| 252 | |||
| 253 | extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath( | ||
| 254 | __in_z LPCWSTR wzBundleId, | ||
| 255 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 256 | ) | ||
| 257 | { | ||
| 258 | HRESULT hr = S_OK; | ||
| 259 | LPWSTR sczWorkingFolder = NULL; | ||
| 260 | |||
| 261 | hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); | ||
| 262 | ExitOnFailure(hr, "Failed to get working folder for bundle layout."); | ||
| 263 | |||
| 264 | hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0); | ||
| 265 | ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path."); | ||
| 266 | |||
| 267 | LExit: | ||
| 268 | ReleaseStr(sczWorkingFolder); | ||
| 269 | |||
| 270 | return hr; | ||
| 271 | } | ||
| 272 | |||
| 273 | extern "C" HRESULT CacheCalculatePayloadWorkingPath( | ||
| 274 | __in_z LPCWSTR wzBundleId, | ||
| 275 | __in BURN_PAYLOAD* pPayload, | ||
| 276 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 277 | ) | ||
| 278 | { | ||
| 279 | HRESULT hr = S_OK; | ||
| 280 | |||
| 281 | hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); | ||
| 282 | ExitOnFailure(hr, "Failed to get working folder for payload."); | ||
| 283 | |||
| 284 | hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0); | ||
| 285 | ExitOnFailure(hr, "Failed to append SHA1 hash as payload unverified path."); | ||
| 286 | |||
| 287 | LExit: | ||
| 288 | return hr; | ||
| 289 | } | ||
| 290 | |||
| 291 | extern "C" HRESULT CacheCalculateContainerWorkingPath( | ||
| 292 | __in_z LPCWSTR wzBundleId, | ||
| 293 | __in BURN_CONTAINER* pContainer, | ||
| 294 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 295 | ) | ||
| 296 | { | ||
| 297 | HRESULT hr = S_OK; | ||
| 298 | |||
| 299 | hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath); | ||
| 300 | ExitOnFailure(hr, "Failed to get working folder for container."); | ||
| 301 | |||
| 302 | hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0); | ||
| 303 | ExitOnFailure(hr, "Failed to append SHA1 hash as container unverified path."); | ||
| 304 | |||
| 305 | LExit: | ||
| 306 | return hr; | ||
| 307 | } | ||
| 308 | |||
| 309 | extern "C" HRESULT CacheGetRootCompletedPath( | ||
| 310 | __in BOOL fPerMachine, | ||
| 311 | __in BOOL fForceInitialize, | ||
| 312 | __deref_out_z LPWSTR* psczRootCompletedPath | ||
| 313 | ) | ||
| 314 | { | ||
| 315 | HRESULT hr = S_OK; | ||
| 316 | |||
| 317 | if (fForceInitialize) | ||
| 318 | { | ||
| 319 | hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath); | ||
| 320 | } | ||
| 321 | else | ||
| 322 | { | ||
| 323 | hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath); | ||
| 324 | } | ||
| 325 | |||
| 326 | return hr; | ||
| 327 | } | ||
| 328 | |||
| 329 | extern "C" HRESULT CacheGetCompletedPath( | ||
| 330 | __in BOOL fPerMachine, | ||
| 331 | __in_z LPCWSTR wzCacheId, | ||
| 332 | __deref_out_z LPWSTR* psczCompletedPath | ||
| 333 | ) | ||
| 334 | { | ||
| 335 | HRESULT hr = S_OK; | ||
| 336 | BOOL fRedirected = FALSE; | ||
| 337 | LPWSTR sczRootPath = NULL; | ||
| 338 | LPWSTR sczCurrentCompletedPath = NULL; | ||
| 339 | LPWSTR sczDefaultCompletedPath = NULL; | ||
| 340 | |||
| 341 | hr = GetRootPath(fPerMachine, TRUE, &sczRootPath); | ||
| 342 | ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 343 | |||
| 344 | // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. | ||
| 345 | fRedirected = S_FALSE == hr; | ||
| 346 | |||
| 347 | hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath); | ||
| 348 | ExitOnFailure(hr, "Failed to construct cache path."); | ||
| 349 | |||
| 350 | hr = PathBackslashTerminate(&sczCurrentCompletedPath); | ||
| 351 | ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); | ||
| 352 | |||
| 353 | // Return the old package cache directory if the new directory does not exist but the old directory does. | ||
| 354 | // If neither package cache directory exists return the (possibly) redirected package cache directory. | ||
| 355 | if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL)) | ||
| 356 | { | ||
| 357 | hr = GetRootPath(fPerMachine, FALSE, &sczRootPath); | ||
| 358 | ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 359 | |||
| 360 | hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath); | ||
| 361 | ExitOnFailure(hr, "Failed to construct cache path."); | ||
| 362 | |||
| 363 | hr = PathBackslashTerminate(&sczDefaultCompletedPath); | ||
| 364 | ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated."); | ||
| 365 | |||
| 366 | if (DirExists(sczDefaultCompletedPath, NULL)) | ||
| 367 | { | ||
| 368 | *psczCompletedPath = sczDefaultCompletedPath; | ||
| 369 | sczDefaultCompletedPath = NULL; | ||
| 370 | |||
| 371 | ExitFunction(); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | *psczCompletedPath = sczCurrentCompletedPath; | ||
| 376 | sczCurrentCompletedPath = NULL; | ||
| 377 | |||
| 378 | LExit: | ||
| 379 | ReleaseNullStr(sczDefaultCompletedPath); | ||
| 380 | ReleaseNullStr(sczCurrentCompletedPath); | ||
| 381 | ReleaseNullStr(sczRootPath); | ||
| 382 | |||
| 383 | return hr; | ||
| 384 | } | ||
| 385 | |||
| 386 | extern "C" HRESULT CacheGetResumePath( | ||
| 387 | __in_z LPCWSTR wzPayloadWorkingPath, | ||
| 388 | __deref_out_z LPWSTR* psczResumePath | ||
| 389 | ) | ||
| 390 | { | ||
| 391 | HRESULT hr = S_OK; | ||
| 392 | |||
| 393 | hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath); | ||
| 394 | ExitOnFailure(hr, "Failed to create resume path."); | ||
| 395 | |||
| 396 | LExit: | ||
| 397 | return hr; | ||
| 398 | } | ||
| 399 | |||
| 400 | extern "C" HRESULT CacheFindLocalSource( | ||
| 401 | __in_z LPCWSTR wzSourcePath, | ||
| 402 | __in BURN_VARIABLES* pVariables, | ||
| 403 | __out BOOL* pfFound, | ||
| 404 | __out_z LPWSTR* psczSourceFullPath | ||
| 405 | ) | ||
| 406 | { | ||
| 407 | HRESULT hr = S_OK; | ||
| 408 | LPWSTR sczSourceProcessFolder = NULL; | ||
| 409 | LPWSTR sczCurrentPath = NULL; | ||
| 410 | LPWSTR sczLastSourcePath = NULL; | ||
| 411 | LPWSTR sczLastSourceFolder = NULL; | ||
| 412 | LPWSTR sczLayoutPath = NULL; | ||
| 413 | LPWSTR sczLayoutFolder = NULL; | ||
| 414 | LPCWSTR rgwzSearchPaths[3] = { }; | ||
| 415 | DWORD cSearchPaths = 0; | ||
| 416 | |||
| 417 | // If the source path provided is a full path, obviously that is where we should be looking. | ||
| 418 | if (PathIsAbsolute(wzSourcePath)) | ||
| 419 | { | ||
| 420 | rgwzSearchPaths[0] = wzSourcePath; | ||
| 421 | cSearchPaths = 1; | ||
| 422 | } | ||
| 423 | else | ||
| 424 | { | ||
| 425 | // If we're not running from cache or we couldn't get the last source, use | ||
| 426 | // the source path location first. In the case where we are in the bundle's | ||
| 427 | // package cache and couldn't find a last used source we unfortunately will | ||
| 428 | // be picking the package cache path which isn't likely to have what we are | ||
| 429 | // looking for. | ||
| 430 | hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder); | ||
| 431 | if (!vfRunningFromCache || FAILED(hr)) | ||
| 432 | { | ||
| 433 | hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder); | ||
| 434 | ExitOnFailure(hr, "Failed to get current process directory."); | ||
| 435 | |||
| 436 | hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath); | ||
| 437 | ExitOnFailure(hr, "Failed to combine last source with source."); | ||
| 438 | |||
| 439 | rgwzSearchPaths[0] = sczCurrentPath; | ||
| 440 | cSearchPaths = 1; | ||
| 441 | } | ||
| 442 | |||
| 443 | // If we have a last used source and it does not duplicate the existing search path, | ||
| 444 | // add the last used source to the search path second. | ||
| 445 | if (sczLastSourceFolder && *sczLastSourceFolder) | ||
| 446 | { | ||
| 447 | hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath); | ||
| 448 | ExitOnFailure(hr, "Failed to combine last source with source."); | ||
| 449 | |||
| 450 | if (0 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[0], -1, sczLastSourcePath, -1)) | ||
| 451 | { | ||
| 452 | rgwzSearchPaths[cSearchPaths] = sczLastSourcePath; | ||
| 453 | ++cSearchPaths; | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | // Also consider the layout directory if set on the command line or by the BA. | ||
| 458 | hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder); | ||
| 459 | if (E_NOTFOUND != hr) | ||
| 460 | { | ||
| 461 | ExitOnFailure(hr, "Failed to get bundle layout directory property."); | ||
| 462 | |||
| 463 | hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath); | ||
| 464 | ExitOnFailure(hr, "Failed to combine layout source with source."); | ||
| 465 | |||
| 466 | rgwzSearchPaths[cSearchPaths] = sczLayoutPath; | ||
| 467 | ++cSearchPaths; | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | *pfFound = FALSE; // assume we won't find the file locally. | ||
| 472 | |||
| 473 | for (DWORD i = 0; i < cSearchPaths; ++i) | ||
| 474 | { | ||
| 475 | // If the file exists locally, copy its path. | ||
| 476 | if (FileExistsEx(rgwzSearchPaths[i], NULL)) | ||
| 477 | { | ||
| 478 | hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0); | ||
| 479 | ExitOnFailure(hr, "Failed to copy source path."); | ||
| 480 | |||
| 481 | *pfFound = TRUE; | ||
| 482 | break; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | // If nothing was found, return the first thing in our search path as the | ||
| 487 | // best path where we thought we should have found the file. | ||
| 488 | if (!*pfFound) | ||
| 489 | { | ||
| 490 | hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0); | ||
| 491 | ExitOnFailure(hr, "Failed to copy source path."); | ||
| 492 | } | ||
| 493 | |||
| 494 | LExit: | ||
| 495 | ReleaseStr(sczCurrentPath); | ||
| 496 | ReleaseStr(sczSourceProcessFolder); | ||
| 497 | ReleaseStr(sczLastSourceFolder); | ||
| 498 | ReleaseStr(sczLastSourcePath); | ||
| 499 | ReleaseStr(sczLayoutFolder); | ||
| 500 | ReleaseStr(sczLayoutPath); | ||
| 501 | |||
| 502 | return hr; | ||
| 503 | } | ||
| 504 | |||
| 505 | extern "C" HRESULT CacheSetLastUsedSource( | ||
| 506 | __in BURN_VARIABLES* pVariables, | ||
| 507 | __in_z LPCWSTR wzSourcePath, | ||
| 508 | __in_z LPCWSTR wzRelativePath | ||
| 509 | ) | ||
| 510 | { | ||
| 511 | HRESULT hr = S_OK; | ||
| 512 | size_t cchSourcePath = 0; | ||
| 513 | size_t cchRelativePath = 0; | ||
| 514 | size_t iSourceRelativePath = 0; | ||
| 515 | LPWSTR sczSourceFolder = NULL; | ||
| 516 | LPWSTR sczLastSourceFolder = NULL; | ||
| 517 | int nCompare = 0; | ||
| 518 | |||
| 519 | hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath); | ||
| 520 | ExitOnFailure(hr, "Failed to determine length of source path."); | ||
| 521 | |||
| 522 | hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath); | ||
| 523 | ExitOnFailure(hr, "Failed to determine length of relative path."); | ||
| 524 | |||
| 525 | // If the source path is smaller than the relative path (plus space for "X:\") then we know they | ||
| 526 | // are not relative to each other. | ||
| 527 | if (cchSourcePath < cchRelativePath + 3) | ||
| 528 | { | ||
| 529 | ExitFunction(); | ||
| 530 | } | ||
| 531 | |||
| 532 | // If the source path ends with the relative path then this source could be a new path. | ||
| 533 | iSourceRelativePath = cchSourcePath - cchRelativePath; | ||
| 534 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1)) | ||
| 535 | { | ||
| 536 | hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath); | ||
| 537 | ExitOnFailure(hr, "Failed to trim source folder."); | ||
| 538 | |||
| 539 | hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder); | ||
| 540 | if (SUCCEEDED(hr)) | ||
| 541 | { | ||
| 542 | nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1); | ||
| 543 | } | ||
| 544 | else if (E_NOTFOUND == hr) | ||
| 545 | { | ||
| 546 | nCompare = CSTR_GREATER_THAN; | ||
| 547 | hr = S_OK; | ||
| 548 | } | ||
| 549 | |||
| 550 | if (CSTR_EQUAL != nCompare) | ||
| 551 | { | ||
| 552 | hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE); | ||
| 553 | ExitOnFailure(hr, "Failed to set last source."); | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | LExit: | ||
| 558 | ReleaseStr(sczLastSourceFolder); | ||
| 559 | ReleaseStr(sczSourceFolder); | ||
| 560 | |||
| 561 | return hr; | ||
| 562 | } | ||
| 563 | |||
| 564 | extern "C" HRESULT CacheSendProgressCallback( | ||
| 565 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 566 | __in DWORD64 dw64Progress, | ||
| 567 | __in DWORD64 dw64Total, | ||
| 568 | __in HANDLE hDestinationFile | ||
| 569 | ) | ||
| 570 | { | ||
| 571 | static LARGE_INTEGER LARGE_INTEGER_ZERO = { }; | ||
| 572 | |||
| 573 | HRESULT hr = S_OK; | ||
| 574 | DWORD dwResult = PROGRESS_CONTINUE; | ||
| 575 | LARGE_INTEGER liTotalSize = { }; | ||
| 576 | LARGE_INTEGER liTotalTransferred = { }; | ||
| 577 | |||
| 578 | if (pCallback->pfnProgress) | ||
| 579 | { | ||
| 580 | liTotalSize.QuadPart = dw64Total; | ||
| 581 | liTotalTransferred.QuadPart = dw64Progress; | ||
| 582 | |||
| 583 | dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv); | ||
| 584 | switch (dwResult) | ||
| 585 | { | ||
| 586 | case PROGRESS_CONTINUE: | ||
| 587 | hr = S_OK; | ||
| 588 | break; | ||
| 589 | |||
| 590 | case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently? | ||
| 591 | case PROGRESS_STOP: | ||
| 592 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 593 | ExitOnRootFailure(hr, "UX aborted on download progress."); | ||
| 594 | |||
| 595 | case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress. | ||
| 596 | pCallback->pfnProgress = NULL; | ||
| 597 | hr = S_OK; | ||
| 598 | break; | ||
| 599 | |||
| 600 | default: | ||
| 601 | hr = E_UNEXPECTED; | ||
| 602 | ExitOnRootFailure(hr, "Invalid return code from progress routine."); | ||
| 603 | } | ||
| 604 | } | ||
| 605 | |||
| 606 | LExit: | ||
| 607 | return hr; | ||
| 608 | } | ||
| 609 | |||
| 610 | extern "C" void CacheSendErrorCallback( | ||
| 611 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 612 | __in HRESULT hrError, | ||
| 613 | __in_z_opt LPCWSTR wzError, | ||
| 614 | __out_opt BOOL* pfRetry | ||
| 615 | ) | ||
| 616 | { | ||
| 617 | if (pfRetry) | ||
| 618 | { | ||
| 619 | *pfRetry = FALSE; | ||
| 620 | } | ||
| 621 | |||
| 622 | if (pCallback->pfnCancel) | ||
| 623 | { | ||
| 624 | int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv); | ||
| 625 | if (pfRetry && IDRETRY == nResult) | ||
| 626 | { | ||
| 627 | *pfRetry = TRUE; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | extern "C" BOOL CacheBundleRunningFromCache() | ||
| 633 | { | ||
| 634 | return vfRunningFromCache; | ||
| 635 | } | ||
| 636 | |||
| 637 | extern "C" HRESULT CacheBundleToCleanRoom( | ||
| 638 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 639 | __in BURN_SECTION* pSection, | ||
| 640 | __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath | ||
| 641 | ) | ||
| 642 | { | ||
| 643 | HRESULT hr = S_OK; | ||
| 644 | LPWSTR sczSourcePath = NULL; | ||
| 645 | LPWSTR wzExecutableName = NULL; | ||
| 646 | |||
| 647 | hr = PathForCurrentProcess(&sczSourcePath, NULL); | ||
| 648 | ExitOnFailure(hr, "Failed to get current path for process to cache to clean room."); | ||
| 649 | |||
| 650 | wzExecutableName = PathFile(sczSourcePath); | ||
| 651 | |||
| 652 | hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczCleanRoomBundlePath); | ||
| 653 | ExitOnFailure(hr, "Failed to cache bundle to clean room."); | ||
| 654 | |||
| 655 | LExit: | ||
| 656 | ReleaseStr(sczSourcePath); | ||
| 657 | |||
| 658 | return hr; | ||
| 659 | } | ||
| 660 | |||
| 661 | extern "C" HRESULT CacheBundleToWorkingDirectory( | ||
| 662 | __in_z LPCWSTR /*wzBundleId*/, | ||
| 663 | __in_z LPCWSTR wzExecutableName, | ||
| 664 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 665 | __in BURN_SECTION* pSection, | ||
| 666 | __deref_out_z_opt LPWSTR* psczEngineWorkingPath | ||
| 667 | ) | ||
| 668 | { | ||
| 669 | Assert(vfInitializedCache); | ||
| 670 | |||
| 671 | HRESULT hr = S_OK; | ||
| 672 | LPWSTR sczSourcePath = NULL; | ||
| 673 | |||
| 674 | // Initialize the source. | ||
| 675 | hr = PathForCurrentProcess(&sczSourcePath, NULL); | ||
| 676 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 677 | |||
| 678 | // If the bundle is running out of the package cache then we don't need to copy it to | ||
| 679 | // the working folder since we feel safe in the package cache and will run from there. | ||
| 680 | if (vfRunningFromCache) | ||
| 681 | { | ||
| 682 | hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0); | ||
| 683 | ExitOnFailure(hr, "Failed to use current process path as target path."); | ||
| 684 | } | ||
| 685 | else // otherwise, carry on putting the bundle in the working folder. | ||
| 686 | { | ||
| 687 | hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczEngineWorkingPath); | ||
| 688 | ExitOnFailure(hr, "Failed to copy engine to working folder."); | ||
| 689 | } | ||
| 690 | |||
| 691 | LExit: | ||
| 692 | ReleaseStr(sczSourcePath); | ||
| 693 | |||
| 694 | return hr; | ||
| 695 | } | ||
| 696 | |||
| 697 | extern "C" HRESULT CacheLayoutBundle( | ||
| 698 | __in_z LPCWSTR wzExecutableName, | ||
| 699 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 700 | __in_z LPCWSTR wzSourceBundlePath | ||
| 701 | ) | ||
| 702 | { | ||
| 703 | HRESULT hr = S_OK; | ||
| 704 | LPWSTR sczTargetPath = NULL; | ||
| 705 | |||
| 706 | hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath); | ||
| 707 | ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout."); | ||
| 708 | |||
| 709 | LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); | ||
| 710 | |||
| 711 | hr = FileEnsureMoveWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 712 | ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); | ||
| 713 | |||
| 714 | LExit: | ||
| 715 | ReleaseStr(sczTargetPath); | ||
| 716 | |||
| 717 | return hr; | ||
| 718 | } | ||
| 719 | |||
| 720 | extern "C" HRESULT CacheCompleteBundle( | ||
| 721 | __in BOOL fPerMachine, | ||
| 722 | __in_z LPCWSTR wzExecutableName, | ||
| 723 | __in_z LPCWSTR wzBundleId, | ||
| 724 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 725 | __in_z LPCWSTR wzSourceBundlePath | ||
| 726 | #ifdef DEBUG | ||
| 727 | , __in_z LPCWSTR wzExecutablePath | ||
| 728 | #endif | ||
| 729 | ) | ||
| 730 | { | ||
| 731 | HRESULT hr = S_OK; | ||
| 732 | int nCompare = 0; | ||
| 733 | LPWSTR sczTargetDirectory = NULL; | ||
| 734 | LPWSTR sczTargetPath = NULL; | ||
| 735 | LPWSTR sczSourceDirectory = NULL; | ||
| 736 | LPWSTR sczPayloadSourcePath = NULL; | ||
| 737 | |||
| 738 | hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory); | ||
| 739 | ExitOnFailure(hr, "Failed to create completed cache path for bundle."); | ||
| 740 | |||
| 741 | hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); | ||
| 742 | ExitOnFailure(hr, "Failed to combine completed path with engine file name."); | ||
| 743 | |||
| 744 | Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1)); | ||
| 745 | |||
| 746 | // If the bundle is running out of the package cache then we don't need to copy it there | ||
| 747 | // (and don't want to since it'll be in use) so bail. | ||
| 748 | hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare); | ||
| 749 | ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath); | ||
| 750 | |||
| 751 | if (CSTR_EQUAL == nCompare) | ||
| 752 | { | ||
| 753 | ExitFunction(); | ||
| 754 | } | ||
| 755 | |||
| 756 | // Otherwise, carry on putting the bundle in the cache. | ||
| 757 | LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath); | ||
| 758 | |||
| 759 | FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart. | ||
| 760 | |||
| 761 | hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 762 | ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath); | ||
| 763 | |||
| 764 | // Reset the path permissions in the cache. | ||
| 765 | hr = ResetPathPermissions(fPerMachine, sczTargetPath); | ||
| 766 | ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath); | ||
| 767 | |||
| 768 | hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory); | ||
| 769 | ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath); | ||
| 770 | |||
| 771 | // Cache external UX payloads to completed path. | ||
| 772 | for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) | ||
| 773 | { | ||
| 774 | BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; | ||
| 775 | |||
| 776 | if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) | ||
| 777 | { | ||
| 778 | hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); | ||
| 779 | ExitOnFailure(hr, "Failed to build payload source path."); | ||
| 780 | |||
| 781 | hr = CacheCompletePayload(fPerMachine, pPayload, wzBundleId, sczPayloadSourcePath, FALSE); | ||
| 782 | ExitOnFailure(hr, "Failed to complete the cache of payload: %ls", pPayload->sczKey); | ||
| 783 | } | ||
| 784 | } | ||
| 785 | |||
| 786 | LExit: | ||
| 787 | ReleaseStr(sczPayloadSourcePath); | ||
| 788 | ReleaseStr(sczSourceDirectory); | ||
| 789 | ReleaseStr(sczTargetPath); | ||
| 790 | ReleaseStr(sczTargetDirectory); | ||
| 791 | |||
| 792 | return hr; | ||
| 793 | } | ||
| 794 | |||
| 795 | extern "C" HRESULT CacheLayoutContainer( | ||
| 796 | __in BURN_CONTAINER* pContainer, | ||
| 797 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 798 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
| 799 | __in BOOL fMove | ||
| 800 | ) | ||
| 801 | { | ||
| 802 | HRESULT hr = S_OK; | ||
| 803 | LPWSTR sczCachedPath = NULL; | ||
| 804 | |||
| 805 | hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath); | ||
| 806 | ExitOnFailure(hr, "Failed to concat complete cached path."); | ||
| 807 | |||
| 808 | hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove); | ||
| 809 | ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath); | ||
| 810 | |||
| 811 | LExit: | ||
| 812 | ReleaseStr(sczCachedPath); | ||
| 813 | |||
| 814 | return hr; | ||
| 815 | } | ||
| 816 | |||
| 817 | extern "C" HRESULT CacheLayoutPayload( | ||
| 818 | __in BURN_PAYLOAD* pPayload, | ||
| 819 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 820 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 821 | __in BOOL fMove | ||
| 822 | ) | ||
| 823 | { | ||
| 824 | HRESULT hr = S_OK; | ||
| 825 | LPWSTR sczCachedPath = NULL; | ||
| 826 | |||
| 827 | hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath); | ||
| 828 | ExitOnFailure(hr, "Failed to concat complete cached path."); | ||
| 829 | |||
| 830 | hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove); | ||
| 831 | ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath); | ||
| 832 | |||
| 833 | LExit: | ||
| 834 | ReleaseStr(sczCachedPath); | ||
| 835 | |||
| 836 | return hr; | ||
| 837 | } | ||
| 838 | |||
| 839 | extern "C" HRESULT CacheCompletePayload( | ||
| 840 | __in BOOL fPerMachine, | ||
| 841 | __in BURN_PAYLOAD* pPayload, | ||
| 842 | __in_z_opt LPCWSTR wzCacheId, | ||
| 843 | __in_z LPCWSTR wzWorkingPayloadPath, | ||
| 844 | __in BOOL fMove | ||
| 845 | ) | ||
| 846 | { | ||
| 847 | HRESULT hr = S_OK; | ||
| 848 | LPWSTR sczCachedDirectory = NULL; | ||
| 849 | LPWSTR sczCachedPath = NULL; | ||
| 850 | LPWSTR sczUnverifiedPayloadPath = NULL; | ||
| 851 | |||
| 852 | hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory); | ||
| 853 | ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId); | ||
| 854 | |||
| 855 | hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath); | ||
| 856 | ExitOnFailure(hr, "Failed to concat complete cached path."); | ||
| 857 | |||
| 858 | // If the cached file matches what we expected, we're good. | ||
| 859 | hr = VerifyFileAgainstPayload(pPayload, sczCachedPath); | ||
| 860 | if (SUCCEEDED(hr)) | ||
| 861 | { | ||
| 862 | ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. | ||
| 863 | LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, sczCachedPath); | ||
| 864 | ExitFunction(); | ||
| 865 | } | ||
| 866 | else if (E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr) | ||
| 867 | { | ||
| 868 | LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczCachedPath, NULL); | ||
| 869 | |||
| 870 | FileEnsureDelete(sczCachedPath); // if the file existed but did not verify correctly, make it go away. | ||
| 871 | } | ||
| 872 | |||
| 873 | hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath); | ||
| 874 | ExitOnFailure(hr, "Failed to create unverified path."); | ||
| 875 | |||
| 876 | // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file. | ||
| 877 | if (FileExistsEx(wzWorkingPayloadPath, NULL)) | ||
| 878 | { | ||
| 879 | hr = TransferWorkingPathToUnverifiedPath(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove); | ||
| 880 | ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey); | ||
| 881 | } | ||
| 882 | else if (!FileExistsEx(sczUnverifiedPayloadPath, NULL)) // if the working path and unverified path do not exist, nothing we can do. | ||
| 883 | { | ||
| 884 | hr = E_FILENOTFOUND; | ||
| 885 | ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath); | ||
| 886 | } | ||
| 887 | |||
| 888 | hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath); | ||
| 889 | ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey); | ||
| 890 | |||
| 891 | hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath); | ||
| 892 | if (FAILED(hr)) | ||
| 893 | { | ||
| 894 | LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, NULL); | ||
| 895 | |||
| 896 | FileEnsureDelete(sczUnverifiedPayloadPath); // if the file did not verify correctly, make it go away. | ||
| 897 | ExitFunction(); | ||
| 898 | } | ||
| 899 | |||
| 900 | LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath); | ||
| 901 | |||
| 902 | hr = FileEnsureMoveWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 903 | ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath); | ||
| 904 | |||
| 905 | ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted. | ||
| 906 | |||
| 907 | LExit: | ||
| 908 | ReleaseStr(sczUnverifiedPayloadPath); | ||
| 909 | ReleaseStr(sczCachedPath); | ||
| 910 | ReleaseStr(sczCachedDirectory); | ||
| 911 | |||
| 912 | return hr; | ||
| 913 | } | ||
| 914 | |||
| 915 | extern "C" HRESULT CacheRemoveWorkingFolder( | ||
| 916 | __in_z_opt LPCWSTR wzBundleId | ||
| 917 | ) | ||
| 918 | { | ||
| 919 | HRESULT hr = S_OK; | ||
| 920 | LPWSTR sczWorkingFolder = NULL; | ||
| 921 | |||
| 922 | if (vfInitializedCache) | ||
| 923 | { | ||
| 924 | hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder); | ||
| 925 | ExitOnFailure(hr, "Failed to calculate the working folder to remove it."); | ||
| 926 | |||
| 927 | // Try to clean out everything in the working folder. | ||
| 928 | hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); | ||
| 929 | TraceError(hr, "Could not delete bundle engine working folder."); | ||
| 930 | } | ||
| 931 | |||
| 932 | LExit: | ||
| 933 | ReleaseStr(sczWorkingFolder); | ||
| 934 | |||
| 935 | return hr; | ||
| 936 | } | ||
| 937 | |||
| 938 | extern "C" HRESULT CacheRemoveBundle( | ||
| 939 | __in BOOL fPerMachine, | ||
| 940 | __in_z LPCWSTR wzBundleId | ||
| 941 | ) | ||
| 942 | { | ||
| 943 | HRESULT hr = S_OK; | ||
| 944 | |||
| 945 | hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId); | ||
| 946 | ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId); | ||
| 947 | |||
| 948 | LExit: | ||
| 949 | return hr; | ||
| 950 | } | ||
| 951 | |||
| 952 | extern "C" HRESULT CacheRemovePackage( | ||
| 953 | __in BOOL fPerMachine, | ||
| 954 | __in_z LPCWSTR wzPackageId, | ||
| 955 | __in_z LPCWSTR wzCacheId | ||
| 956 | ) | ||
| 957 | { | ||
| 958 | HRESULT hr = S_OK; | ||
| 959 | |||
| 960 | hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId); | ||
| 961 | ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId); | ||
| 962 | |||
| 963 | LExit: | ||
| 964 | return hr; | ||
| 965 | } | ||
| 966 | |||
| 967 | extern "C" HRESULT CacheVerifyPayloadSignature( | ||
| 968 | __in BURN_PAYLOAD* pPayload, | ||
| 969 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 970 | __in HANDLE hFile | ||
| 971 | ) | ||
| 972 | { | ||
| 973 | HRESULT hr = S_OK; | ||
| 974 | LONG er = ERROR_SUCCESS; | ||
| 975 | |||
| 976 | GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; | ||
| 977 | WINTRUST_FILE_INFO wfi = { }; | ||
| 978 | WINTRUST_DATA wtd = { }; | ||
| 979 | CRYPT_PROVIDER_DATA* pProviderData = NULL; | ||
| 980 | CRYPT_PROVIDER_SGNR* pSigner = NULL; | ||
| 981 | |||
| 982 | // Verify the payload assuming online. | ||
| 983 | wfi.cbStruct = sizeof(wfi); | ||
| 984 | wfi.pcwszFilePath = wzUnverifiedPayloadPath; | ||
| 985 | wfi.hFile = hFile; | ||
| 986 | |||
| 987 | wtd.cbStruct = sizeof(wtd); | ||
| 988 | wtd.dwUnionChoice = WTD_CHOICE_FILE; | ||
| 989 | wtd.pFile = &wfi; | ||
| 990 | wtd.dwStateAction = WTD_STATEACTION_VERIFY; | ||
| 991 | wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; | ||
| 992 | wtd.dwUIChoice = WTD_UI_NONE; | ||
| 993 | |||
| 994 | er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); | ||
| 995 | if (er) | ||
| 996 | { | ||
| 997 | // Verify the payload assuming offline. | ||
| 998 | wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; | ||
| 999 | |||
| 1000 | er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); | ||
| 1001 | ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | pProviderData = WTHelperProvDataFromStateData(wtd.hWVTStateData); | ||
| 1005 | ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate."); | ||
| 1006 | |||
| 1007 | pSigner = WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0); | ||
| 1008 | ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate."); | ||
| 1009 | |||
| 1010 | hr = VerifyPayloadAgainstChain(pPayload, pSigner->pChainContext); | ||
| 1011 | ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain."); | ||
| 1012 | |||
| 1013 | LExit: | ||
| 1014 | return hr; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | extern "C" void CacheCleanup( | ||
| 1018 | __in BOOL fPerMachine, | ||
| 1019 | __in_z LPCWSTR wzBundleId | ||
| 1020 | ) | ||
| 1021 | { | ||
| 1022 | HRESULT hr = S_OK; | ||
| 1023 | LPWSTR sczFolder = NULL; | ||
| 1024 | LPWSTR sczFiles = NULL; | ||
| 1025 | LPWSTR sczDelete = NULL; | ||
| 1026 | HANDLE hFind = INVALID_HANDLE_VALUE; | ||
| 1027 | WIN32_FIND_DATAW wfd = { }; | ||
| 1028 | DWORD cFileName = 0; | ||
| 1029 | |||
| 1030 | hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder); | ||
| 1031 | if (SUCCEEDED(hr)) | ||
| 1032 | { | ||
| 1033 | hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | if (!fPerMachine) | ||
| 1037 | { | ||
| 1038 | hr = CalculateWorkingFolder(wzBundleId, &sczFolder); | ||
| 1039 | if (SUCCEEDED(hr)) | ||
| 1040 | { | ||
| 1041 | hr = PathConcat(sczFolder, L"*.*", &sczFiles); | ||
| 1042 | if (SUCCEEDED(hr)) | ||
| 1043 | { | ||
| 1044 | hFind = ::FindFirstFileW(sczFiles, &wfd); | ||
| 1045 | if (INVALID_HANDLE_VALUE != hFind) | ||
| 1046 | { | ||
| 1047 | do | ||
| 1048 | { | ||
| 1049 | // Skip directories. | ||
| 1050 | if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | ||
| 1051 | { | ||
| 1052 | continue; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | // For extra safety and to silence OACR. | ||
| 1056 | wfd.cFileName[MAX_PATH - 1] = L'\0'; | ||
| 1057 | |||
| 1058 | // Skip resume files (they end with ".R"). | ||
| 1059 | cFileName = lstrlenW(wfd.cFileName); | ||
| 1060 | if (2 < cFileName && L'.' == wfd.cFileName[cFileName - 2] && (L'R' == wfd.cFileName[cFileName - 1] || L'r' == wfd.cFileName[cFileName - 1])) | ||
| 1061 | { | ||
| 1062 | continue; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | hr = PathConcat(sczFolder, wfd.cFileName, &sczDelete); | ||
| 1066 | if (SUCCEEDED(hr)) | ||
| 1067 | { | ||
| 1068 | hr = FileEnsureDelete(sczDelete); | ||
| 1069 | } | ||
| 1070 | } while (::FindNextFileW(hFind, &wfd)); | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | if (INVALID_HANDLE_VALUE != hFind) | ||
| 1077 | { | ||
| 1078 | ::FindClose(hFind); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | ReleaseStr(sczDelete); | ||
| 1082 | ReleaseStr(sczFiles); | ||
| 1083 | ReleaseStr(sczFolder); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | extern "C" void CacheUninitialize() | ||
| 1087 | { | ||
| 1088 | ReleaseNullStr(vsczCurrentMachinePackageCache); | ||
| 1089 | ReleaseNullStr(vsczDefaultMachinePackageCache); | ||
| 1090 | ReleaseNullStr(vsczDefaultUserPackageCache); | ||
| 1091 | ReleaseNullStr(vsczWorkingFolder); | ||
| 1092 | ReleaseNullStr(vsczSourceProcessPath); | ||
| 1093 | |||
| 1094 | vfRunningFromCache = FALSE; | ||
| 1095 | vfInitializedCache = FALSE; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | // Internal functions. | ||
| 1099 | |||
| 1100 | static HRESULT CalculateWorkingFolder( | ||
| 1101 | __in_z LPCWSTR /*wzBundleId*/, | ||
| 1102 | __deref_out_z LPWSTR* psczWorkingFolder | ||
| 1103 | ) | ||
| 1104 | { | ||
| 1105 | HRESULT hr = S_OK; | ||
| 1106 | RPC_STATUS rs = RPC_S_OK; | ||
| 1107 | BOOL fElevated = FALSE; | ||
| 1108 | WCHAR wzTempPath[MAX_PATH] = { }; | ||
| 1109 | UUID guid = {}; | ||
| 1110 | WCHAR wzGuid[39]; | ||
| 1111 | |||
| 1112 | if (!vsczWorkingFolder) | ||
| 1113 | { | ||
| 1114 | ProcElevated(::GetCurrentProcess(), &fElevated); | ||
| 1115 | |||
| 1116 | if (fElevated) | ||
| 1117 | { | ||
| 1118 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
| 1119 | { | ||
| 1120 | ExitWithLastError(hr, "Failed to get windows path for working folder."); | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath)); | ||
| 1124 | ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash."); | ||
| 1125 | |||
| 1126 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\"); | ||
| 1127 | ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder."); | ||
| 1128 | } | ||
| 1129 | else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
| 1130 | { | ||
| 1131 | ExitWithLastError(hr, "Failed to get temp path for working folder."); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | rs = ::UuidCreate(&guid); | ||
| 1135 | hr = HRESULT_FROM_RPC(rs); | ||
| 1136 | ExitOnFailure(hr, "Failed to create working folder guid."); | ||
| 1137 | |||
| 1138 | if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid))) | ||
| 1139 | { | ||
| 1140 | hr = E_OUTOFMEMORY; | ||
| 1141 | ExitOnRootFailure(hr, "Failed to convert working folder guid into string."); | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid); | ||
| 1145 | ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder."); | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0); | ||
| 1149 | ExitOnFailure(hr, "Failed to copy working folder path."); | ||
| 1150 | |||
| 1151 | LExit: | ||
| 1152 | return hr; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | static HRESULT GetRootPath( | ||
| 1156 | __in BOOL fPerMachine, | ||
| 1157 | __in BOOL fAllowRedirect, | ||
| 1158 | __deref_out_z LPWSTR* psczRootPath | ||
| 1159 | ) | ||
| 1160 | { | ||
| 1161 | HRESULT hr = S_OK; | ||
| 1162 | LPWSTR sczAppData = NULL; | ||
| 1163 | int nCompare = 0; | ||
| 1164 | |||
| 1165 | // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. | ||
| 1166 | if (fPerMachine) | ||
| 1167 | { | ||
| 1168 | // Always construct the default machine package cache path so we can determine if we're redirected. | ||
| 1169 | if (!vsczDefaultMachinePackageCache) | ||
| 1170 | { | ||
| 1171 | hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData); | ||
| 1172 | ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine"); | ||
| 1173 | |||
| 1174 | hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache); | ||
| 1175 | ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine"); | ||
| 1176 | |||
| 1177 | hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache); | ||
| 1178 | ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine"); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if (!vsczCurrentMachinePackageCache) | ||
| 1182 | { | ||
| 1183 | hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache); | ||
| 1184 | ExitOnFailure(hr, "Failed to read PackageCache policy directory."); | ||
| 1185 | |||
| 1186 | if (vsczCurrentMachinePackageCache) | ||
| 1187 | { | ||
| 1188 | hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache); | ||
| 1189 | ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name."); | ||
| 1190 | } | ||
| 1191 | else | ||
| 1192 | { | ||
| 1193 | hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0); | ||
| 1194 | ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory."); | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0); | ||
| 1199 | ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine"); | ||
| 1200 | |||
| 1201 | hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare); | ||
| 1202 | ExitOnFailure(hr, "Failed to compare default and current package cache directories."); | ||
| 1203 | |||
| 1204 | // Return S_FALSE if the current location is not the default location (redirected). | ||
| 1205 | hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE; | ||
| 1206 | } | ||
| 1207 | else | ||
| 1208 | { | ||
| 1209 | if (!vsczDefaultUserPackageCache) | ||
| 1210 | { | ||
| 1211 | hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); | ||
| 1212 | ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user"); | ||
| 1213 | |||
| 1214 | hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache); | ||
| 1215 | ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user"); | ||
| 1216 | |||
| 1217 | hr = PathBackslashTerminate(&vsczDefaultUserPackageCache); | ||
| 1218 | ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user"); | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0); | ||
| 1222 | ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user"); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | LExit: | ||
| 1226 | ReleaseStr(sczAppData); | ||
| 1227 | |||
| 1228 | return hr; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | static HRESULT GetLastUsedSourceFolder( | ||
| 1232 | __in BURN_VARIABLES* pVariables, | ||
| 1233 | __out_z LPWSTR* psczLastSource | ||
| 1234 | ) | ||
| 1235 | { | ||
| 1236 | HRESULT hr = S_OK; | ||
| 1237 | LPWSTR sczOriginalSource = NULL; | ||
| 1238 | |||
| 1239 | hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource); | ||
| 1240 | if (E_NOTFOUND == hr) | ||
| 1241 | { | ||
| 1242 | // Try the original source folder. | ||
| 1243 | hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource); | ||
| 1244 | if (SUCCEEDED(hr)) | ||
| 1245 | { | ||
| 1246 | hr = PathGetDirectory(sczOriginalSource, psczLastSource); | ||
| 1247 | } | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | return hr; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | static HRESULT CreateCompletedPath( | ||
| 1254 | __in BOOL fPerMachine, | ||
| 1255 | __in LPCWSTR wzId, | ||
| 1256 | __out LPWSTR* psczCacheDirectory | ||
| 1257 | ) | ||
| 1258 | { | ||
| 1259 | static BOOL fPerMachineCacheRootVerified = FALSE; | ||
| 1260 | |||
| 1261 | HRESULT hr = S_OK; | ||
| 1262 | LPWSTR sczCacheDirectory = NULL; | ||
| 1263 | |||
| 1264 | // If we are doing a permachine install but have not yet verified that the root cache folder | ||
| 1265 | // was created with the correct ACLs yet, do that now. | ||
| 1266 | if (fPerMachine && !fPerMachineCacheRootVerified) | ||
| 1267 | { | ||
| 1268 | hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory); | ||
| 1269 | ExitOnFailure(hr, "Failed to get cache directory."); | ||
| 1270 | |||
| 1271 | hr = DirEnsureExists(sczCacheDirectory, NULL); | ||
| 1272 | ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); | ||
| 1273 | |||
| 1274 | hr = SecurePath(sczCacheDirectory); | ||
| 1275 | ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory); | ||
| 1276 | |||
| 1277 | fPerMachineCacheRootVerified = TRUE; | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | // Get the cache completed path, ensure it exists, and reset any permissions people | ||
| 1281 | // might have tried to set on the directory so we inherit the (correct!) security | ||
| 1282 | // permissions from the parent directory. | ||
| 1283 | hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory); | ||
| 1284 | ExitOnFailure(hr, "Failed to get cache directory."); | ||
| 1285 | |||
| 1286 | hr = DirEnsureExists(sczCacheDirectory, NULL); | ||
| 1287 | ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory); | ||
| 1288 | |||
| 1289 | ResetPathPermissions(fPerMachine, sczCacheDirectory); | ||
| 1290 | |||
| 1291 | *psczCacheDirectory = sczCacheDirectory; | ||
| 1292 | sczCacheDirectory = NULL; | ||
| 1293 | |||
| 1294 | LExit: | ||
| 1295 | ReleaseStr(sczCacheDirectory); | ||
| 1296 | return hr; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | static HRESULT CreateUnverifiedPath( | ||
| 1300 | __in BOOL fPerMachine, | ||
| 1301 | __in_z LPCWSTR wzPayloadId, | ||
| 1302 | __out_z LPWSTR* psczUnverifiedPayloadPath | ||
| 1303 | ) | ||
| 1304 | { | ||
| 1305 | static BOOL fUnverifiedCacheFolderCreated = FALSE; | ||
| 1306 | |||
| 1307 | HRESULT hr = S_OK; | ||
| 1308 | LPWSTR sczUnverifiedCacheFolder = NULL; | ||
| 1309 | |||
| 1310 | hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder); | ||
| 1311 | ExitOnFailure(hr, "Failed to get cache directory."); | ||
| 1312 | |||
| 1313 | if (!fUnverifiedCacheFolderCreated) | ||
| 1314 | { | ||
| 1315 | hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL); | ||
| 1316 | ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder); | ||
| 1317 | |||
| 1318 | ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder); | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath); | ||
| 1322 | ExitOnFailure(hr, "Failed to concat payload id to unverified folder path."); | ||
| 1323 | |||
| 1324 | LExit: | ||
| 1325 | ReleaseStr(sczUnverifiedCacheFolder); | ||
| 1326 | |||
| 1327 | return hr; | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | static HRESULT VerifyThenTransferContainer( | ||
| 1331 | __in BURN_CONTAINER* pContainer, | ||
| 1332 | __in_z LPCWSTR wzCachedPath, | ||
| 1333 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
| 1334 | __in BOOL fMove | ||
| 1335 | ) | ||
| 1336 | { | ||
| 1337 | HRESULT hr = S_OK; | ||
| 1338 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
| 1339 | |||
| 1340 | // Get the container on disk actual hash. | ||
| 1341 | hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 1342 | if (INVALID_HANDLE_VALUE == hFile) | ||
| 1343 | { | ||
| 1344 | ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath); | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | // Container should have a hash we can use to verify with. | ||
| 1348 | if (pContainer->pbHash) | ||
| 1349 | { | ||
| 1350 | hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, wzUnverifiedContainerPath, hFile); | ||
| 1351 | ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath); | ||
| 1355 | |||
| 1356 | if (fMove) | ||
| 1357 | { | ||
| 1358 | hr = FileEnsureMoveWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1359 | ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); | ||
| 1360 | } | ||
| 1361 | else | ||
| 1362 | { | ||
| 1363 | hr = FileEnsureCopyWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1364 | ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedContainerPath, wzCachedPath); | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | LExit: | ||
| 1368 | ReleaseFileHandle(hFile); | ||
| 1369 | |||
| 1370 | return hr; | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | static HRESULT VerifyThenTransferPayload( | ||
| 1374 | __in BURN_PAYLOAD* pPayload, | ||
| 1375 | __in_z LPCWSTR wzCachedPath, | ||
| 1376 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 1377 | __in BOOL fMove | ||
| 1378 | ) | ||
| 1379 | { | ||
| 1380 | HRESULT hr = S_OK; | ||
| 1381 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
| 1382 | |||
| 1383 | // Get the payload on disk actual hash. | ||
| 1384 | hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 1385 | if (INVALID_HANDLE_VALUE == hFile) | ||
| 1386 | { | ||
| 1387 | ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath); | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | // If the payload has a certificate root public key identifier provided, verify the certificate. | ||
| 1391 | if (pPayload->pbCertificateRootPublicKeyIdentifier) | ||
| 1392 | { | ||
| 1393 | hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile); | ||
| 1394 | ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); | ||
| 1395 | } | ||
| 1396 | else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file | ||
| 1397 | { | ||
| 1398 | hr = VerifyPayloadWithCatalog(pPayload, wzUnverifiedPayloadPath, hFile); | ||
| 1399 | ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath); | ||
| 1400 | } | ||
| 1401 | else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. | ||
| 1402 | { | ||
| 1403 | hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile); | ||
| 1404 | ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath); | ||
| 1408 | |||
| 1409 | if (fMove) | ||
| 1410 | { | ||
| 1411 | hr = FileEnsureMoveWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1412 | ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); | ||
| 1413 | } | ||
| 1414 | else | ||
| 1415 | { | ||
| 1416 | hr = FileEnsureCopyWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1417 | ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | LExit: | ||
| 1421 | ReleaseFileHandle(hFile); | ||
| 1422 | |||
| 1423 | return hr; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static HRESULT TransferWorkingPathToUnverifiedPath( | ||
| 1427 | __in_z LPCWSTR wzWorkingPath, | ||
| 1428 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 1429 | __in BOOL fMove | ||
| 1430 | ) | ||
| 1431 | { | ||
| 1432 | HRESULT hr = S_OK; | ||
| 1433 | |||
| 1434 | if (fMove) | ||
| 1435 | { | ||
| 1436 | hr = FileEnsureMoveWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1437 | ExitOnFailure(hr, "Failed to move %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); | ||
| 1438 | } | ||
| 1439 | else | ||
| 1440 | { | ||
| 1441 | hr = FileEnsureCopyWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1442 | ExitOnFailure(hr, "Failed to copy %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath); | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | LExit: | ||
| 1446 | return hr; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | static HRESULT VerifyFileAgainstPayload( | ||
| 1450 | __in BURN_PAYLOAD* pPayload, | ||
| 1451 | __in_z LPCWSTR wzVerifyPath | ||
| 1452 | ) | ||
| 1453 | { | ||
| 1454 | HRESULT hr = S_OK; | ||
| 1455 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
| 1456 | |||
| 1457 | // Get the payload on disk actual hash. | ||
| 1458 | hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 1459 | if (INVALID_HANDLE_VALUE == hFile) | ||
| 1460 | { | ||
| 1461 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 1462 | if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr) | ||
| 1463 | { | ||
| 1464 | ExitFunction(); // do not log error when the file was not found. | ||
| 1465 | } | ||
| 1466 | ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath); | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | // If the payload has a certificate root public key identifier provided, verify the certificate. | ||
| 1470 | if (pPayload->pbCertificateRootPublicKeyIdentifier) | ||
| 1471 | { | ||
| 1472 | hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile); | ||
| 1473 | ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey); | ||
| 1474 | } | ||
| 1475 | else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file | ||
| 1476 | { | ||
| 1477 | hr = VerifyPayloadWithCatalog(pPayload, wzVerifyPath, hFile); | ||
| 1478 | ExitOnFailure(hr, "Failed to verify catalog signature of payload: %ls", pPayload->sczKey); | ||
| 1479 | } | ||
| 1480 | else if (pPayload->pbHash) // the payload should have a hash we can use to verify it. | ||
| 1481 | { | ||
| 1482 | hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile); | ||
| 1483 | ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | LExit: | ||
| 1487 | ReleaseFileHandle(hFile); | ||
| 1488 | |||
| 1489 | return hr; | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | static HRESULT AllocateSid( | ||
| 1493 | __in WELL_KNOWN_SID_TYPE type, | ||
| 1494 | __out PSID* ppSid | ||
| 1495 | ) | ||
| 1496 | { | ||
| 1497 | HRESULT hr = S_OK; | ||
| 1498 | PSID pAllocSid = NULL; | ||
| 1499 | DWORD cbSid = SECURITY_MAX_SID_SIZE; | ||
| 1500 | |||
| 1501 | pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE)); | ||
| 1502 | ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID."); | ||
| 1503 | |||
| 1504 | if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid)) | ||
| 1505 | { | ||
| 1506 | ExitWithLastError(hr, "Failed to create well known SID."); | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | *ppSid = pAllocSid; | ||
| 1510 | pAllocSid = NULL; | ||
| 1511 | |||
| 1512 | LExit: | ||
| 1513 | ReleaseMem(pAllocSid); | ||
| 1514 | return hr; | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | |||
| 1518 | static HRESULT ResetPathPermissions( | ||
| 1519 | __in BOOL fPerMachine, | ||
| 1520 | __in LPCWSTR wzPath | ||
| 1521 | ) | ||
| 1522 | { | ||
| 1523 | HRESULT hr = S_OK; | ||
| 1524 | DWORD er = ERROR_SUCCESS; | ||
| 1525 | DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; | ||
| 1526 | ACL acl = { }; | ||
| 1527 | PSID pSid = NULL; | ||
| 1528 | |||
| 1529 | if (fPerMachine) | ||
| 1530 | { | ||
| 1531 | hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid); | ||
| 1532 | ExitOnFailure(hr, "Failed to allocate administrator SID."); | ||
| 1533 | |||
| 1534 | // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent. | ||
| 1535 | if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION)) | ||
| 1536 | { | ||
| 1537 | ExitWithLastError(hr, "Failed to initialize ACL."); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | dwSetSecurity |= OWNER_SECURITY_INFORMATION; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1544 | ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath); | ||
| 1545 | |||
| 1546 | ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits. | ||
| 1547 | |||
| 1548 | LExit: | ||
| 1549 | ReleaseMem(pSid); | ||
| 1550 | return hr; | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | |||
| 1554 | static HRESULT GrantAccessAndAllocateSid( | ||
| 1555 | __in WELL_KNOWN_SID_TYPE type, | ||
| 1556 | __in DWORD dwGrantAccess, | ||
| 1557 | __in EXPLICIT_ACCESS* pAccess | ||
| 1558 | ) | ||
| 1559 | { | ||
| 1560 | HRESULT hr = S_OK; | ||
| 1561 | |||
| 1562 | hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName)); | ||
| 1563 | ExitOnFailure(hr, "Failed to allocate SID to grate access."); | ||
| 1564 | |||
| 1565 | pAccess->grfAccessMode = GRANT_ACCESS; | ||
| 1566 | pAccess->grfAccessPermissions = dwGrantAccess; | ||
| 1567 | pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; | ||
| 1568 | pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID; | ||
| 1569 | pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP; | ||
| 1570 | |||
| 1571 | LExit: | ||
| 1572 | return hr; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | |||
| 1576 | static HRESULT SecurePath( | ||
| 1577 | __in LPCWSTR wzPath | ||
| 1578 | ) | ||
| 1579 | { | ||
| 1580 | HRESULT hr = S_OK; | ||
| 1581 | DWORD er = ERROR_SUCCESS; | ||
| 1582 | EXPLICIT_ACCESSW access[4] = { }; | ||
| 1583 | PACL pAcl = NULL; | ||
| 1584 | |||
| 1585 | // Administrators must be the first one in the array so we can reuse the allocated SID below. | ||
| 1586 | hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]); | ||
| 1587 | ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath); | ||
| 1588 | |||
| 1589 | hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]); | ||
| 1590 | ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath); | ||
| 1591 | |||
| 1592 | hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]); | ||
| 1593 | ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath); | ||
| 1594 | |||
| 1595 | hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]); | ||
| 1596 | ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath); | ||
| 1597 | |||
| 1598 | er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl); | ||
| 1599 | ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath); | ||
| 1600 | |||
| 1601 | // Set the ACL and ensure the Administrators group ends up the owner | ||
| 1602 | hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, | ||
| 1603 | reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1604 | ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath); | ||
| 1605 | |||
| 1606 | LExit: | ||
| 1607 | if (pAcl) | ||
| 1608 | { | ||
| 1609 | ::LocalFree(pAcl); | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | for (DWORD i = 0; i < countof(access); ++i) | ||
| 1613 | { | ||
| 1614 | ReleaseMem(access[i].Trustee.ptstrName); | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | return hr; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | |||
| 1621 | static HRESULT CopyEngineToWorkingFolder( | ||
| 1622 | __in_z LPCWSTR wzSourcePath, | ||
| 1623 | __in_z LPCWSTR wzWorkingFolderName, | ||
| 1624 | __in_z LPCWSTR wzExecutableName, | ||
| 1625 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 1626 | __in BURN_SECTION* pSection, | ||
| 1627 | __deref_out_z_opt LPWSTR* psczEngineWorkingPath | ||
| 1628 | ) | ||
| 1629 | { | ||
| 1630 | HRESULT hr = S_OK; | ||
| 1631 | LPWSTR sczWorkingFolder = NULL; | ||
| 1632 | LPWSTR sczTargetDirectory = NULL; | ||
| 1633 | LPWSTR sczTargetPath = NULL; | ||
| 1634 | LPWSTR sczSourceDirectory = NULL; | ||
| 1635 | LPWSTR sczPayloadSourcePath = NULL; | ||
| 1636 | LPWSTR sczPayloadTargetPath = NULL; | ||
| 1637 | |||
| 1638 | hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder); | ||
| 1639 | ExitOnFailure(hr, "Failed to create working path to copy engine."); | ||
| 1640 | |||
| 1641 | hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory); | ||
| 1642 | ExitOnFailure(hr, "Failed to calculate the bundle working folder target name."); | ||
| 1643 | |||
| 1644 | hr = DirEnsureExists(sczTargetDirectory, NULL); | ||
| 1645 | ExitOnFailure(hr, "Failed create bundle working folder."); | ||
| 1646 | |||
| 1647 | hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath); | ||
| 1648 | ExitOnFailure(hr, "Failed to combine working path with engine file name."); | ||
| 1649 | |||
| 1650 | // Copy the engine without any attached containers to the working path. | ||
| 1651 | hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection); | ||
| 1652 | ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath); | ||
| 1653 | |||
| 1654 | // Copy external UX payloads to working path. | ||
| 1655 | for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i) | ||
| 1656 | { | ||
| 1657 | BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i]; | ||
| 1658 | |||
| 1659 | if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging) | ||
| 1660 | { | ||
| 1661 | if (!sczSourceDirectory) | ||
| 1662 | { | ||
| 1663 | hr = PathGetDirectory(wzSourcePath, &sczSourceDirectory); | ||
| 1664 | ExitOnFailure(hr, "Failed to get directory from engine path: %ls", wzSourcePath); | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath); | ||
| 1668 | ExitOnFailure(hr, "Failed to build payload source path for working copy."); | ||
| 1669 | |||
| 1670 | hr = PathConcat(sczTargetDirectory, pPayload->sczFilePath, &sczPayloadTargetPath); | ||
| 1671 | ExitOnFailure(hr, "Failed to build payload target path for working copy."); | ||
| 1672 | |||
| 1673 | hr = FileEnsureCopyWithRetry(sczPayloadSourcePath, sczPayloadTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT); | ||
| 1674 | ExitOnFailure(hr, "Failed to copy UX payload from: '%ls' to: '%ls'", sczPayloadSourcePath, sczPayloadTargetPath); | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | if (psczEngineWorkingPath) | ||
| 1679 | { | ||
| 1680 | hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0); | ||
| 1681 | ExitOnFailure(hr, "Failed to copy target path for engine working path."); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | LExit: | ||
| 1685 | ReleaseStr(sczPayloadTargetPath); | ||
| 1686 | ReleaseStr(sczPayloadSourcePath); | ||
| 1687 | ReleaseStr(sczSourceDirectory); | ||
| 1688 | ReleaseStr(sczTargetPath); | ||
| 1689 | ReleaseStr(sczTargetDirectory); | ||
| 1690 | ReleaseStr(sczWorkingFolder); | ||
| 1691 | |||
| 1692 | return hr; | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | |||
| 1696 | static HRESULT CopyEngineWithSignatureFixup( | ||
| 1697 | __in HANDLE hEngineFile, | ||
| 1698 | __in_z LPCWSTR wzEnginePath, | ||
| 1699 | __in_z LPCWSTR wzTargetPath, | ||
| 1700 | __in BURN_SECTION* pSection | ||
| 1701 | ) | ||
| 1702 | { | ||
| 1703 | HRESULT hr = S_OK; | ||
| 1704 | HANDLE hTarget = INVALID_HANDLE_VALUE; | ||
| 1705 | LARGE_INTEGER li = { }; | ||
| 1706 | DWORD dwZeroOriginals[3] = { }; | ||
| 1707 | |||
| 1708 | hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 1709 | if (INVALID_HANDLE_VALUE == hTarget) | ||
| 1710 | { | ||
| 1711 | ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath); | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN); | ||
| 1715 | ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath); | ||
| 1716 | |||
| 1717 | hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL); | ||
| 1718 | ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath); | ||
| 1719 | |||
| 1720 | // If the original executable was signed, let's put back the checksum and signature. | ||
| 1721 | if (pSection->dwOriginalSignatureOffset) | ||
| 1722 | { | ||
| 1723 | // Fix up the checksum. | ||
| 1724 | li.QuadPart = pSection->dwChecksumOffset; | ||
| 1725 | if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) | ||
| 1726 | { | ||
| 1727 | ExitWithLastError(hr, "Failed to seek to checksum in exe header."); | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum)); | ||
| 1731 | ExitOnFailure(hr, "Failed to update signature offset."); | ||
| 1732 | |||
| 1733 | // Fix up the signature information. | ||
| 1734 | li.QuadPart = pSection->dwCertificateTableOffset; | ||
| 1735 | if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) | ||
| 1736 | { | ||
| 1737 | ExitWithLastError(hr, "Failed to seek to signature table in exe header."); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset)); | ||
| 1741 | ExitOnFailure(hr, "Failed to update signature offset."); | ||
| 1742 | |||
| 1743 | hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize)); | ||
| 1744 | ExitOnFailure(hr, "Failed to update signature offset."); | ||
| 1745 | |||
| 1746 | // Zero out the original information since that is how it was when the file was originally signed. | ||
| 1747 | li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset; | ||
| 1748 | if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN)) | ||
| 1749 | { | ||
| 1750 | ExitWithLastError(hr, "Failed to seek to original data in exe burn section header."); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals)); | ||
| 1754 | ExitOnFailure(hr, "Failed to zero out original data offset."); | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | LExit: | ||
| 1758 | ReleaseFileHandle(hTarget); | ||
| 1759 | |||
| 1760 | return hr; | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | |||
| 1764 | static HRESULT RemoveBundleOrPackage( | ||
| 1765 | __in BOOL fBundle, | ||
| 1766 | __in BOOL fPerMachine, | ||
| 1767 | __in_z LPCWSTR wzBundleOrPackageId, | ||
| 1768 | __in_z LPCWSTR wzCacheId | ||
| 1769 | ) | ||
| 1770 | { | ||
| 1771 | HRESULT hr = S_OK; | ||
| 1772 | LPWSTR sczRootCacheDirectory = NULL; | ||
| 1773 | LPWSTR sczDirectory = NULL; | ||
| 1774 | |||
| 1775 | hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory); | ||
| 1776 | ExitOnFailure(hr, "Failed to calculate cache path."); | ||
| 1777 | |||
| 1778 | LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory); | ||
| 1779 | |||
| 1780 | // Try really hard to remove the cache directory. | ||
| 1781 | hr = E_FAIL; | ||
| 1782 | for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry) | ||
| 1783 | { | ||
| 1784 | if (0 < iRetry) | ||
| 1785 | { | ||
| 1786 | ::Sleep(FILE_OPERATION_RETRY_WAIT); | ||
| 1787 | } | ||
| 1788 | |||
| 1789 | hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); | ||
| 1790 | if (E_PATHNOTFOUND == hr) | ||
| 1791 | { | ||
| 1792 | break; | ||
| 1793 | } | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | if (FAILED(hr)) | ||
| 1797 | { | ||
| 1798 | LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr); | ||
| 1799 | hr = S_OK; | ||
| 1800 | } | ||
| 1801 | else | ||
| 1802 | { | ||
| 1803 | // Try to remove root package cache in the off chance it is now empty. | ||
| 1804 | hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory); | ||
| 1805 | ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 1806 | DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); | ||
| 1807 | |||
| 1808 | // GetRootPath returns S_FALSE if the package cache is redirected elsewhere. | ||
| 1809 | if (S_FALSE == hr) | ||
| 1810 | { | ||
| 1811 | hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory); | ||
| 1812 | ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 1813 | DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE); | ||
| 1814 | } | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | LExit: | ||
| 1818 | ReleaseStr(sczDirectory); | ||
| 1819 | ReleaseStr(sczRootCacheDirectory); | ||
| 1820 | |||
| 1821 | return hr; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | static HRESULT VerifyHash( | ||
| 1825 | __in BYTE* pbHash, | ||
| 1826 | __in DWORD cbHash, | ||
| 1827 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 1828 | __in HANDLE hFile | ||
| 1829 | ) | ||
| 1830 | { | ||
| 1831 | UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath); | ||
| 1832 | |||
| 1833 | HRESULT hr = S_OK; | ||
| 1834 | BYTE rgbActualHash[SHA1_HASH_LEN] = { }; | ||
| 1835 | DWORD64 qwHashedBytes; | ||
| 1836 | LPWSTR pszExpected = NULL; | ||
| 1837 | LPWSTR pszActual = NULL; | ||
| 1838 | |||
| 1839 | // TODO: create a cryp hash file that sends progress. | ||
| 1840 | hr = CrypHashFileHandle(hFile, PROV_RSA_FULL, CALG_SHA1, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes); | ||
| 1841 | ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath); | ||
| 1842 | |||
| 1843 | // Compare hashes. | ||
| 1844 | if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA1_HASH_LEN)) | ||
| 1845 | { | ||
| 1846 | hr = CRYPT_E_HASH_VALUE; | ||
| 1847 | |||
| 1848 | // Best effort to log the expected and actual hash value strings. | ||
| 1849 | if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) && | ||
| 1850 | SUCCEEDED(StrAllocHexEncode(rgbActualHash, SHA1_HASH_LEN, &pszActual))) | ||
| 1851 | { | ||
| 1852 | ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual); | ||
| 1853 | } | ||
| 1854 | else | ||
| 1855 | { | ||
| 1856 | ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath); | ||
| 1857 | } | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | LExit: | ||
| 1861 | ReleaseStr(pszActual); | ||
| 1862 | ReleaseStr(pszExpected); | ||
| 1863 | |||
| 1864 | return hr; | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | static HRESULT VerifyPayloadWithCatalog( | ||
| 1868 | __in BURN_PAYLOAD* pPayload, | ||
| 1869 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 1870 | __in HANDLE hFile | ||
| 1871 | ) | ||
| 1872 | { | ||
| 1873 | HRESULT hr = S_FALSE; | ||
| 1874 | DWORD er = ERROR_SUCCESS; | ||
| 1875 | WINTRUST_DATA WinTrustData = { }; | ||
| 1876 | WINTRUST_CATALOG_INFO WinTrustCatalogInfo = { }; | ||
| 1877 | GUID gSubSystemDriver = WINTRUST_ACTION_GENERIC_VERIFY_V2; | ||
| 1878 | LPWSTR sczLowerCaseFile = NULL; | ||
| 1879 | LPWSTR pCurrent = NULL; | ||
| 1880 | LPWSTR sczName = NULL; | ||
| 1881 | DWORD dwHashSize = 0; | ||
| 1882 | DWORD dwTagSize; | ||
| 1883 | LPBYTE pbHash = NULL; | ||
| 1884 | |||
| 1885 | // Get lower case file name. Older operating systems need a lower case file | ||
| 1886 | // to match in the catalog | ||
| 1887 | hr = StrAllocString(&sczLowerCaseFile, wzUnverifiedPayloadPath, 0); | ||
| 1888 | ExitOnFailure(hr, "Failed to allocate memory"); | ||
| 1889 | |||
| 1890 | // Go through each character doing the lower case of each letter | ||
| 1891 | pCurrent = sczLowerCaseFile; | ||
| 1892 | while ('\0' != *pCurrent) | ||
| 1893 | { | ||
| 1894 | *pCurrent = (WCHAR)_tolower(*pCurrent); | ||
| 1895 | pCurrent++; | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | // Get file hash | ||
| 1899 | CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0); | ||
| 1900 | er = ::GetLastError(); | ||
| 1901 | if (ERROR_INSUFFICIENT_BUFFER == er) | ||
| 1902 | { | ||
| 1903 | pbHash = (LPBYTE)MemAlloc(dwHashSize, TRUE); | ||
| 1904 | if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0)) | ||
| 1905 | { | ||
| 1906 | ExitWithLastError(hr, "Failed to get file hash."); | ||
| 1907 | } | ||
| 1908 | } | ||
| 1909 | else | ||
| 1910 | { | ||
| 1911 | ExitOnWin32Error(er, hr, "Failed to get file hash."); | ||
| 1912 | } | ||
| 1913 | |||
| 1914 | // Make the hash into a string. This is the member tag for the catalog | ||
| 1915 | dwTagSize = (dwHashSize * 2) + 1; | ||
| 1916 | hr = StrAlloc(&sczName, dwTagSize); | ||
| 1917 | ExitOnFailure(hr, "Failed to allocate string."); | ||
| 1918 | hr = StrHexEncode(pbHash, dwHashSize, sczName, dwTagSize); | ||
| 1919 | ExitOnFailure(hr, "Failed to encode file hash."); | ||
| 1920 | |||
| 1921 | // Set up the WinVerifyTrust structures assuming online. | ||
| 1922 | WinTrustData.cbStruct = sizeof(WINTRUST_DATA); | ||
| 1923 | WinTrustData.dwUIChoice = WTD_UI_NONE; | ||
| 1924 | WinTrustData.dwUnionChoice = WTD_CHOICE_CATALOG; | ||
| 1925 | WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY; | ||
| 1926 | WinTrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; | ||
| 1927 | WinTrustData.pCatalog = &WinTrustCatalogInfo; | ||
| 1928 | |||
| 1929 | WinTrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO); | ||
| 1930 | WinTrustCatalogInfo.pbCalculatedFileHash = pbHash; | ||
| 1931 | WinTrustCatalogInfo.cbCalculatedFileHash = dwHashSize; | ||
| 1932 | WinTrustCatalogInfo.hMemberFile = hFile; | ||
| 1933 | WinTrustCatalogInfo.pcwszMemberTag = sczName; | ||
| 1934 | WinTrustCatalogInfo.pcwszMemberFilePath = sczLowerCaseFile; | ||
| 1935 | WinTrustCatalogInfo.pcwszCatalogFilePath = pPayload->pCatalog->sczLocalFilePath; | ||
| 1936 | |||
| 1937 | hr = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); | ||
| 1938 | if (hr) | ||
| 1939 | { | ||
| 1940 | // Set up the WinVerifyTrust structures assuming online. | ||
| 1941 | WinTrustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL; | ||
| 1942 | |||
| 1943 | er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); | ||
| 1944 | |||
| 1945 | // WinVerifyTrust returns 0 for success, a few different Win32 error codes if it can't | ||
| 1946 | // find the provider, and any other error code is provider specific, so may not | ||
| 1947 | // be an actual Win32 error code | ||
| 1948 | ExitOnWin32Error(er, hr, "Could not verify file %ls.", wzUnverifiedPayloadPath); | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | // Need to close the WinVerifyTrust action | ||
| 1952 | WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE; | ||
| 1953 | er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData); | ||
| 1954 | ExitOnWin32Error(er, hr, "Could not close verify handle."); | ||
| 1955 | |||
| 1956 | LExit: | ||
| 1957 | ReleaseStr(sczLowerCaseFile); | ||
| 1958 | ReleaseStr(sczName); | ||
| 1959 | ReleaseMem(pbHash); | ||
| 1960 | |||
| 1961 | return hr; | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | static HRESULT VerifyPayloadAgainstChain( | ||
| 1965 | __in BURN_PAYLOAD* pPayload, | ||
| 1966 | __in PCCERT_CHAIN_CONTEXT pChainContext | ||
| 1967 | ) | ||
| 1968 | { | ||
| 1969 | HRESULT hr = S_OK; | ||
| 1970 | PCCERT_CONTEXT pChainElementCertContext = NULL; | ||
| 1971 | |||
| 1972 | BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { }; | ||
| 1973 | DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier); | ||
| 1974 | BYTE* pbThumbprint = NULL; | ||
| 1975 | DWORD cbThumbprint = 0; | ||
| 1976 | |||
| 1977 | // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier | ||
| 1978 | // and thumbprint (if a thumbprint was provided). | ||
| 1979 | HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match. | ||
| 1980 | for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i) | ||
| 1981 | { | ||
| 1982 | pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext; | ||
| 1983 | |||
| 1984 | // Get the certificate's public key identifier. | ||
| 1985 | if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier)) | ||
| 1986 | { | ||
| 1987 | ExitWithLastError(hr, "Failed to get certificate public key identifier."); | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | // Compare the certificate's public key identifier with the payload's public key identifier. If they | ||
| 1991 | // match, we're one step closer to the a positive result. | ||
| 1992 | if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier && | ||
| 1993 | 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier)) | ||
| 1994 | { | ||
| 1995 | // If the payload specified a thumbprint for the certificate, verify it. | ||
| 1996 | if (pPayload->pbCertificateRootThumbprint) | ||
| 1997 | { | ||
| 1998 | hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint); | ||
| 1999 | ExitOnFailure(hr, "Failed to read certificate thumbprint."); | ||
| 2000 | |||
| 2001 | if (pPayload->cbCertificateRootThumbprint == cbThumbprint && | ||
| 2002 | 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint)) | ||
| 2003 | { | ||
| 2004 | // If we got here, we found that our payload public key identifier and thumbprint | ||
| 2005 | // matched an element in the certficate chain. | ||
| 2006 | hrChainVerification = S_OK; | ||
| 2007 | break; | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | ReleaseNullMem(pbThumbprint); | ||
| 2011 | } | ||
| 2012 | else // no thumbprint match necessary so we're good to go. | ||
| 2013 | { | ||
| 2014 | hrChainVerification = S_OK; | ||
| 2015 | break; | ||
| 2016 | } | ||
| 2017 | } | ||
| 2018 | } | ||
| 2019 | hr = hrChainVerification; | ||
| 2020 | ExitOnFailure(hr, "Failed to find expected public key in certificate chain."); | ||
| 2021 | |||
| 2022 | LExit: | ||
| 2023 | ReleaseMem(pbThumbprint); | ||
| 2024 | |||
| 2025 | return hr; | ||
| 2026 | } | ||
diff --git a/src/engine/cache.h b/src/engine/cache.h new file mode 100644 index 00000000..95e6cb90 --- /dev/null +++ b/src/engine/cache.h | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | // structs | ||
| 10 | |||
| 11 | // functions | ||
| 12 | |||
| 13 | HRESULT CacheInitialize( | ||
| 14 | __in BURN_REGISTRATION* pRegistration, | ||
| 15 | __in BURN_VARIABLES* pVariables, | ||
| 16 | __in_z_opt LPCWSTR wzSourceProcessPath | ||
| 17 | ); | ||
| 18 | HRESULT CacheEnsureWorkingFolder( | ||
| 19 | __in LPCWSTR wzBundleId, | ||
| 20 | __deref_out_z_opt LPWSTR* psczWorkingFolder | ||
| 21 | ); | ||
| 22 | HRESULT CacheCalculateBundleWorkingPath( | ||
| 23 | __in_z LPCWSTR wzBundleId, | ||
| 24 | __in LPCWSTR wzExecutableName, | ||
| 25 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 26 | ); | ||
| 27 | HRESULT CacheCalculateBundleLayoutWorkingPath( | ||
| 28 | __in_z LPCWSTR wzBundleId, | ||
| 29 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 30 | ); | ||
| 31 | HRESULT CacheCalculatePayloadWorkingPath( | ||
| 32 | __in_z LPCWSTR wzBundleId, | ||
| 33 | __in BURN_PAYLOAD* pPayload, | ||
| 34 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 35 | ); | ||
| 36 | HRESULT CacheCalculateContainerWorkingPath( | ||
| 37 | __in_z LPCWSTR wzBundleId, | ||
| 38 | __in BURN_CONTAINER* pContainer, | ||
| 39 | __deref_out_z LPWSTR* psczWorkingPath | ||
| 40 | ); | ||
| 41 | HRESULT CacheGetRootCompletedPath( | ||
| 42 | __in BOOL fPerMachine, | ||
| 43 | __in BOOL fForceInitialize, | ||
| 44 | __deref_out_z LPWSTR* psczRootCompletedPath | ||
| 45 | ); | ||
| 46 | HRESULT CacheGetCompletedPath( | ||
| 47 | __in BOOL fPerMachine, | ||
| 48 | __in_z LPCWSTR wzCacheId, | ||
| 49 | __deref_out_z LPWSTR* psczCompletedPath | ||
| 50 | ); | ||
| 51 | HRESULT CacheGetResumePath( | ||
| 52 | __in_z LPCWSTR wzPayloadWorkingPath, | ||
| 53 | __deref_out_z LPWSTR* psczResumePath | ||
| 54 | ); | ||
| 55 | HRESULT CacheFindLocalSource( | ||
| 56 | __in_z LPCWSTR wzSourcePath, | ||
| 57 | __in BURN_VARIABLES* pVariables, | ||
| 58 | __out BOOL* pfFound, | ||
| 59 | __out_z LPWSTR* psczSourceFullPath | ||
| 60 | ); | ||
| 61 | HRESULT CacheSetLastUsedSource( | ||
| 62 | __in BURN_VARIABLES* pVariables, | ||
| 63 | __in_z LPCWSTR wzSourcePath, | ||
| 64 | __in_z LPCWSTR wzRelativePath | ||
| 65 | ); | ||
| 66 | HRESULT CacheSendProgressCallback( | ||
| 67 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 68 | __in DWORD64 dw64Progress, | ||
| 69 | __in DWORD64 dw64Total, | ||
| 70 | __in HANDLE hDestinationFile | ||
| 71 | ); | ||
| 72 | void CacheSendErrorCallback( | ||
| 73 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
| 74 | __in HRESULT hrError, | ||
| 75 | __in_z_opt LPCWSTR wzError, | ||
| 76 | __out_opt BOOL* pfRetry | ||
| 77 | ); | ||
| 78 | BOOL CacheBundleRunningFromCache(); | ||
| 79 | HRESULT CacheBundleToCleanRoom( | ||
| 80 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 81 | __in BURN_SECTION* pSection, | ||
| 82 | __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath | ||
| 83 | ); | ||
| 84 | HRESULT CacheBundleToWorkingDirectory( | ||
| 85 | __in_z LPCWSTR wzBundleId, | ||
| 86 | __in_z LPCWSTR wzExecutableName, | ||
| 87 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 88 | __in BURN_SECTION* pSection, | ||
| 89 | __deref_out_z_opt LPWSTR* psczEngineWorkingPath | ||
| 90 | ); | ||
| 91 | HRESULT CacheLayoutBundle( | ||
| 92 | __in_z LPCWSTR wzExecutableName, | ||
| 93 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 94 | __in_z LPCWSTR wzSourceBundlePath | ||
| 95 | ); | ||
| 96 | HRESULT CacheCompleteBundle( | ||
| 97 | __in BOOL fPerMachine, | ||
| 98 | __in_z LPCWSTR wzExecutableName, | ||
| 99 | __in_z LPCWSTR wzBundleId, | ||
| 100 | __in BURN_PAYLOADS* pUxPayloads, | ||
| 101 | __in_z LPCWSTR wzSourceBundlePath | ||
| 102 | #ifdef DEBUG | ||
| 103 | , __in_z LPCWSTR wzExecutablePath | ||
| 104 | #endif | ||
| 105 | ); | ||
| 106 | HRESULT CacheLayoutContainer( | ||
| 107 | __in BURN_CONTAINER* pContainer, | ||
| 108 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 109 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
| 110 | __in BOOL fMove | ||
| 111 | ); | ||
| 112 | HRESULT CacheLayoutPayload( | ||
| 113 | __in BURN_PAYLOAD* pPayload, | ||
| 114 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 115 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 116 | __in BOOL fMove | ||
| 117 | ); | ||
| 118 | HRESULT CacheCompletePayload( | ||
| 119 | __in BOOL fPerMachine, | ||
| 120 | __in BURN_PAYLOAD* pPayload, | ||
| 121 | __in_z_opt LPCWSTR wzCacheId, | ||
| 122 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 123 | __in BOOL fMove | ||
| 124 | ); | ||
| 125 | HRESULT CacheRemoveWorkingFolder( | ||
| 126 | __in_z_opt LPCWSTR wzBundleId | ||
| 127 | ); | ||
| 128 | HRESULT CacheRemoveBundle( | ||
| 129 | __in BOOL fPerMachine, | ||
| 130 | __in_z LPCWSTR wzPackageId | ||
| 131 | ); | ||
| 132 | HRESULT CacheRemovePackage( | ||
| 133 | __in BOOL fPerMachine, | ||
| 134 | __in_z LPCWSTR wzPackageId, | ||
| 135 | __in_z LPCWSTR wzCacheId | ||
| 136 | ); | ||
| 137 | HRESULT CacheVerifyPayloadSignature( | ||
| 138 | __in BURN_PAYLOAD* pPayload, | ||
| 139 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
| 140 | __in HANDLE hFile | ||
| 141 | ); | ||
| 142 | void CacheCleanup( | ||
| 143 | __in BOOL fPerMachine, | ||
| 144 | __in_z LPCWSTR wzBundleId | ||
| 145 | ); | ||
| 146 | void CacheUninitialize(); | ||
| 147 | |||
| 148 | #ifdef __cplusplus | ||
| 149 | } | ||
| 150 | #endif | ||
diff --git a/src/engine/catalog.cpp b/src/engine/catalog.cpp new file mode 100644 index 00000000..da086545 --- /dev/null +++ b/src/engine/catalog.cpp | |||
| @@ -0,0 +1,180 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // function definitions | ||
| 7 | |||
| 8 | extern "C" HRESULT CatalogsParseFromXml( | ||
| 9 | __in BURN_CATALOGS* pCatalogs, | ||
| 10 | __in IXMLDOMNode* pixnBundle | ||
| 11 | ) | ||
| 12 | { | ||
| 13 | HRESULT hr = S_OK; | ||
| 14 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 15 | IXMLDOMNode* pixnNode = NULL; | ||
| 16 | DWORD cNodes = 0; | ||
| 17 | LPWSTR scz = NULL; | ||
| 18 | |||
| 19 | // select catalog nodes | ||
| 20 | hr = XmlSelectNodes(pixnBundle, L"Catalog", &pixnNodes); | ||
| 21 | ExitOnFailure(hr, "Failed to select catalog nodes."); | ||
| 22 | |||
| 23 | // get catalog node count | ||
| 24 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 25 | ExitOnFailure(hr, "Failed to get payload node count."); | ||
| 26 | if (!cNodes) | ||
| 27 | { | ||
| 28 | ExitFunction(); | ||
| 29 | } | ||
| 30 | |||
| 31 | // allocate memory for catalogs | ||
| 32 | pCatalogs->rgCatalogs = (BURN_CATALOG*)MemAlloc(sizeof(BURN_CATALOG) * cNodes, TRUE); | ||
| 33 | ExitOnNull(pCatalogs->rgCatalogs, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); | ||
| 34 | |||
| 35 | pCatalogs->cCatalogs = cNodes; | ||
| 36 | |||
| 37 | // parse catalog elements | ||
| 38 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 39 | { | ||
| 40 | BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; | ||
| 41 | pCatalog->hFile = INVALID_HANDLE_VALUE; | ||
| 42 | |||
| 43 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 44 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 45 | |||
| 46 | // @Id | ||
| 47 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pCatalog->sczKey); | ||
| 48 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 49 | |||
| 50 | // @Payload | ||
| 51 | hr = XmlGetAttributeEx(pixnNode, L"Payload", &pCatalog->sczPayload); | ||
| 52 | ExitOnFailure(hr, "Failed to get @Payload."); | ||
| 53 | |||
| 54 | // prepare next iteration | ||
| 55 | ReleaseNullObject(pixnNode); | ||
| 56 | } | ||
| 57 | |||
| 58 | LExit: | ||
| 59 | ReleaseObject(pixnNodes); | ||
| 60 | ReleaseObject(pixnNode); | ||
| 61 | ReleaseStr(scz); | ||
| 62 | |||
| 63 | return hr; | ||
| 64 | } | ||
| 65 | |||
| 66 | extern "C" HRESULT CatalogFindById( | ||
| 67 | __in BURN_CATALOGS* pCatalogs, | ||
| 68 | __in_z LPCWSTR wzId, | ||
| 69 | __out BURN_CATALOG** ppCatalog | ||
| 70 | ) | ||
| 71 | { | ||
| 72 | HRESULT hr = S_OK; | ||
| 73 | BURN_CATALOG* pCatalog = NULL; | ||
| 74 | |||
| 75 | for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) | ||
| 76 | { | ||
| 77 | pCatalog = &pCatalogs->rgCatalogs[i]; | ||
| 78 | |||
| 79 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCatalog->sczKey, -1, wzId, -1)) | ||
| 80 | { | ||
| 81 | *ppCatalog = pCatalog; | ||
| 82 | ExitFunction1(hr = S_OK); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | hr = E_NOTFOUND; | ||
| 87 | |||
| 88 | LExit: | ||
| 89 | return hr; | ||
| 90 | } | ||
| 91 | |||
| 92 | extern "C" HRESULT CatalogLoadFromPayload( | ||
| 93 | __in BURN_CATALOGS* pCatalogs, | ||
| 94 | __in BURN_PAYLOADS* pPayloads | ||
| 95 | ) | ||
| 96 | { | ||
| 97 | HRESULT hr = S_OK; | ||
| 98 | BURN_CATALOG* pCatalog = NULL; | ||
| 99 | BURN_PAYLOAD* pPayload = NULL; | ||
| 100 | |||
| 101 | // go through each catalog file | ||
| 102 | for (DWORD i = 0; i < pCatalogs->cCatalogs; i++) | ||
| 103 | { | ||
| 104 | pCatalog = &pCatalogs->rgCatalogs[i]; | ||
| 105 | |||
| 106 | // get the payload for this catalog file | ||
| 107 | hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload); | ||
| 108 | ExitOnFailure(hr, "Failed to find payload for catalog file."); | ||
| 109 | |||
| 110 | // Get the local file name | ||
| 111 | hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0); | ||
| 112 | ExitOnFailure(hr, "Failed to get catalog local file path"); | ||
| 113 | |||
| 114 | // Get a handle to the file | ||
| 115 | pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 116 | if (INVALID_HANDLE_VALUE == pCatalog->hFile) | ||
| 117 | { | ||
| 118 | ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Verify the catalog file | ||
| 122 | hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile); | ||
| 123 | ExitOnFailure(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath); | ||
| 124 | } | ||
| 125 | |||
| 126 | LExit: | ||
| 127 | return hr; | ||
| 128 | } | ||
| 129 | |||
| 130 | extern "C" HRESULT CatalogElevatedUpdateCatalogFile( | ||
| 131 | __in BURN_CATALOGS* pCatalogs, | ||
| 132 | __in_z LPCWSTR wzId, | ||
| 133 | __in_z LPCWSTR wzPath | ||
| 134 | ) | ||
| 135 | { | ||
| 136 | HRESULT hr = S_OK; | ||
| 137 | BURN_CATALOG* pCatalog = NULL; | ||
| 138 | |||
| 139 | // Find the catalog | ||
| 140 | hr = CatalogFindById(pCatalogs, wzId, &pCatalog); | ||
| 141 | ExitOnFailure(hr, "Failed to locate catalog information."); | ||
| 142 | |||
| 143 | if (NULL == pCatalog->sczLocalFilePath) | ||
| 144 | { | ||
| 145 | hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0); | ||
| 146 | ExitOnFailure(hr, "Failed to allocated catalog path."); | ||
| 147 | |||
| 148 | // Get a handle to the file | ||
| 149 | pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 150 | if (INVALID_HANDLE_VALUE == pCatalog->hFile) | ||
| 151 | { | ||
| 152 | ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | LExit: | ||
| 157 | return hr; | ||
| 158 | } | ||
| 159 | |||
| 160 | extern "C" void CatalogUninitialize( | ||
| 161 | __in BURN_CATALOGS* pCatalogs | ||
| 162 | ) | ||
| 163 | { | ||
| 164 | if (pCatalogs->rgCatalogs) | ||
| 165 | { | ||
| 166 | for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i) | ||
| 167 | { | ||
| 168 | BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i]; | ||
| 169 | |||
| 170 | ReleaseHandle(pCatalog->hFile); | ||
| 171 | ReleaseStr(pCatalog->sczKey); | ||
| 172 | ReleaseStr(pCatalog->sczLocalFilePath); | ||
| 173 | ReleaseStr(pCatalog->sczPayload); | ||
| 174 | } | ||
| 175 | MemFree(pCatalogs->rgCatalogs); | ||
| 176 | } | ||
| 177 | |||
| 178 | // clear struct | ||
| 179 | memset(pCatalogs, 0, sizeof(BURN_CATALOGS)); | ||
| 180 | } | ||
diff --git a/src/engine/catalog.h b/src/engine/catalog.h new file mode 100644 index 00000000..3a87d0d2 --- /dev/null +++ b/src/engine/catalog.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | // structs | ||
| 10 | |||
| 11 | typedef struct _BURN_CATALOG | ||
| 12 | { | ||
| 13 | LPWSTR sczKey; | ||
| 14 | LPWSTR sczPayload; | ||
| 15 | |||
| 16 | // mutable members | ||
| 17 | LPWSTR sczLocalFilePath; // location of extracted or downloaded copy | ||
| 18 | HANDLE hFile; | ||
| 19 | } BURN_CATALOG; | ||
| 20 | |||
| 21 | typedef struct _BURN_CATALOGS | ||
| 22 | { | ||
| 23 | BURN_CATALOG* rgCatalogs; | ||
| 24 | DWORD cCatalogs; | ||
| 25 | } BURN_CATALOGS; | ||
| 26 | |||
| 27 | typedef struct _BURN_PAYLOADS BURN_PAYLOADS; | ||
| 28 | |||
| 29 | |||
| 30 | // functions | ||
| 31 | |||
| 32 | HRESULT CatalogsParseFromXml( | ||
| 33 | __in BURN_CATALOGS* pCatalogs, | ||
| 34 | __in IXMLDOMNode* pixnBundle | ||
| 35 | ); | ||
| 36 | HRESULT CatalogFindById( | ||
| 37 | __in BURN_CATALOGS* pCatalogs, | ||
| 38 | __in_z LPCWSTR wzId, | ||
| 39 | __out BURN_CATALOG** ppCatalog | ||
| 40 | ); | ||
| 41 | HRESULT CatalogLoadFromPayload( | ||
| 42 | __in BURN_CATALOGS* pCatalogs, | ||
| 43 | __in BURN_PAYLOADS* pPayloads | ||
| 44 | ); | ||
| 45 | HRESULT CatalogElevatedUpdateCatalogFile( | ||
| 46 | __in BURN_CATALOGS* pCatalogs, | ||
| 47 | __in_z LPCWSTR wzId, | ||
| 48 | __in_z LPCWSTR wzPath | ||
| 49 | ); | ||
| 50 | void CatalogUninitialize( | ||
| 51 | __in BURN_CATALOGS* pCatalogs | ||
| 52 | ); | ||
| 53 | |||
| 54 | #if defined(__cplusplus) | ||
| 55 | } | ||
| 56 | #endif | ||
diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp new file mode 100644 index 00000000..28391d2d --- /dev/null +++ b/src/engine/condition.cpp | |||
| @@ -0,0 +1,1030 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // | ||
| 7 | // parse rules | ||
| 8 | // | ||
| 9 | // value variable | literal | integer | version | ||
| 10 | // comparison-operator < | > | <= | >= | = | <> | >< | << | >> | ||
| 11 | // term value | value comparison-operator value | ( expression ) | ||
| 12 | // boolean-factor term | NOT term | ||
| 13 | // boolean-term boolean-factor | boolean-factor AND boolean-term | ||
| 14 | // expression boolean-term | boolean-term OR expression | ||
| 15 | // | ||
| 16 | |||
| 17 | |||
| 18 | // constants | ||
| 19 | |||
| 20 | #define COMPARISON 0x00010000 | ||
| 21 | #define INSENSITIVE 0x00020000 | ||
| 22 | |||
| 23 | enum BURN_SYMBOL_TYPE | ||
| 24 | { | ||
| 25 | // terminals | ||
| 26 | BURN_SYMBOL_TYPE_NONE = 0, | ||
| 27 | BURN_SYMBOL_TYPE_END = 1, | ||
| 28 | BURN_SYMBOL_TYPE_OR = 2, // OR | ||
| 29 | BURN_SYMBOL_TYPE_AND = 3, // AND | ||
| 30 | BURN_SYMBOL_TYPE_NOT = 4, // NOT | ||
| 31 | BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // < | ||
| 32 | BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // > | ||
| 33 | BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <= | ||
| 34 | BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >= | ||
| 35 | BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // = | ||
| 36 | BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <> | ||
| 37 | BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // >< | ||
| 38 | BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // << | ||
| 39 | BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >> | ||
| 40 | BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~< | ||
| 41 | BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~> | ||
| 42 | BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<= | ||
| 43 | BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>= | ||
| 44 | BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~= | ||
| 45 | BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<> | ||
| 46 | BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~>< | ||
| 47 | BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<< | ||
| 48 | BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>> | ||
| 49 | BURN_SYMBOL_TYPE_LPAREN = 14, // ( | ||
| 50 | BURN_SYMBOL_TYPE_RPAREN = 15, // ) | ||
| 51 | BURN_SYMBOL_TYPE_NUMBER = 16, | ||
| 52 | BURN_SYMBOL_TYPE_IDENTIFIER = 17, | ||
| 53 | BURN_SYMBOL_TYPE_LITERAL = 18, | ||
| 54 | BURN_SYMBOL_TYPE_VERSION = 19, | ||
| 55 | }; | ||
| 56 | |||
| 57 | |||
| 58 | // structs | ||
| 59 | |||
| 60 | struct BURN_SYMBOL | ||
| 61 | { | ||
| 62 | BURN_SYMBOL_TYPE Type; | ||
| 63 | DWORD iPosition; | ||
| 64 | BURN_VARIANT Value; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct BURN_CONDITION_PARSE_CONTEXT | ||
| 68 | { | ||
| 69 | BURN_VARIABLES* pVariables; | ||
| 70 | LPCWSTR wzCondition; | ||
| 71 | LPCWSTR wzRead; | ||
| 72 | BURN_SYMBOL NextSymbol; | ||
| 73 | BOOL fError; | ||
| 74 | }; | ||
| 75 | |||
| 76 | |||
| 77 | // internal function declarations | ||
| 78 | |||
| 79 | static HRESULT ParseExpression( | ||
| 80 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 81 | __out BOOL* pf | ||
| 82 | ); | ||
| 83 | static HRESULT ParseBooleanTerm( | ||
| 84 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 85 | __out BOOL* pf | ||
| 86 | ); | ||
| 87 | static HRESULT ParseBooleanFactor( | ||
| 88 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 89 | __out BOOL* pf | ||
| 90 | ); | ||
| 91 | static HRESULT ParseTerm( | ||
| 92 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 93 | __out BOOL* pf | ||
| 94 | ); | ||
| 95 | static HRESULT ParseValue( | ||
| 96 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 97 | __out BURN_VARIANT* pValue | ||
| 98 | ); | ||
| 99 | static HRESULT Expect( | ||
| 100 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 101 | __in BURN_SYMBOL_TYPE symbolType | ||
| 102 | ); | ||
| 103 | static HRESULT NextSymbol( | ||
| 104 | __in BURN_CONDITION_PARSE_CONTEXT* pContext | ||
| 105 | ); | ||
| 106 | static HRESULT CompareValues( | ||
| 107 | __in BURN_SYMBOL_TYPE comparison, | ||
| 108 | __in BURN_VARIANT leftOperand, | ||
| 109 | __in BURN_VARIANT rightOperand, | ||
| 110 | __out BOOL* pfResult | ||
| 111 | ); | ||
| 112 | static HRESULT CompareStringValues( | ||
| 113 | __in BURN_SYMBOL_TYPE comparison, | ||
| 114 | __in_z LPCWSTR wzLeftOperand, | ||
| 115 | __in_z LPCWSTR wzRightOperand, | ||
| 116 | __out BOOL* pfResult | ||
| 117 | ); | ||
| 118 | static HRESULT CompareIntegerValues( | ||
| 119 | __in BURN_SYMBOL_TYPE comparison, | ||
| 120 | __in LONGLONG llLeftOperand, | ||
| 121 | __in LONGLONG llRightOperand, | ||
| 122 | __out BOOL* pfResult | ||
| 123 | ); | ||
| 124 | static HRESULT CompareVersionValues( | ||
| 125 | __in BURN_SYMBOL_TYPE comparison, | ||
| 126 | __in DWORD64 qwLeftOperand, | ||
| 127 | __in DWORD64 qwRightOperand, | ||
| 128 | __out BOOL* pfResult | ||
| 129 | ); | ||
| 130 | |||
| 131 | |||
| 132 | // function definitions | ||
| 133 | |||
| 134 | extern "C" HRESULT ConditionEvaluate( | ||
| 135 | __in BURN_VARIABLES* pVariables, | ||
| 136 | __in_z LPCWSTR wzCondition, | ||
| 137 | __out BOOL* pf | ||
| 138 | ) | ||
| 139 | { | ||
| 140 | HRESULT hr = S_OK; | ||
| 141 | BURN_CONDITION_PARSE_CONTEXT context = { }; | ||
| 142 | BOOL f = FALSE; | ||
| 143 | |||
| 144 | context.pVariables = pVariables; | ||
| 145 | context.wzCondition = wzCondition; | ||
| 146 | context.wzRead = wzCondition; | ||
| 147 | |||
| 148 | hr = NextSymbol(&context); | ||
| 149 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 150 | |||
| 151 | hr = ParseExpression(&context, &f); | ||
| 152 | ExitOnFailure(hr, "Failed to parse expression."); | ||
| 153 | |||
| 154 | hr = Expect(&context, BURN_SYMBOL_TYPE_END); | ||
| 155 | ExitOnFailure(hr, "Failed to expect end symbol."); | ||
| 156 | |||
| 157 | LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f)); | ||
| 158 | |||
| 159 | *pf = f; | ||
| 160 | hr = S_OK; | ||
| 161 | |||
| 162 | LExit: | ||
| 163 | if (context.fError) | ||
| 164 | { | ||
| 165 | Assert(FAILED(hr)); | ||
| 166 | LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL); | ||
| 167 | } | ||
| 168 | |||
| 169 | return hr; | ||
| 170 | } | ||
| 171 | |||
| 172 | extern "C" HRESULT ConditionGlobalCheck( | ||
| 173 | __in BURN_VARIABLES* pVariables, | ||
| 174 | __in BURN_CONDITION* pCondition, | ||
| 175 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 176 | __in_z LPCWSTR wzBundleName, | ||
| 177 | __out DWORD *pdwExitCode, | ||
| 178 | __out BOOL *pfContinueExecution | ||
| 179 | ) | ||
| 180 | { | ||
| 181 | HRESULT hr = S_OK; | ||
| 182 | BOOL fSuccess = TRUE; | ||
| 183 | HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); | ||
| 184 | OS_VERSION osv = OS_VERSION_UNKNOWN; | ||
| 185 | DWORD dwServicePack = 0; | ||
| 186 | |||
| 187 | OsGetVersion(&osv, &dwServicePack); | ||
| 188 | |||
| 189 | // Always error on Windows 2000 or lower | ||
| 190 | if (OS_VERSION_WIN2000 >= osv) | ||
| 191 | { | ||
| 192 | fSuccess = FALSE; | ||
| 193 | } | ||
| 194 | else | ||
| 195 | { | ||
| 196 | if (NULL != pCondition->sczConditionString) | ||
| 197 | { | ||
| 198 | hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess); | ||
| 199 | ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | if (!fSuccess) | ||
| 204 | { | ||
| 205 | // Display the error messagebox, as long as we're in an appropriate display mode | ||
| 206 | hr = SplashScreenDisplayError(display, wzBundleName, hrError); | ||
| 207 | ExitOnFailure(hr, "Failed to display error dialog"); | ||
| 208 | |||
| 209 | *pdwExitCode = static_cast<DWORD>(hrError); | ||
| 210 | *pfContinueExecution = FALSE; | ||
| 211 | } | ||
| 212 | |||
| 213 | LExit: | ||
| 214 | return hr; | ||
| 215 | } | ||
| 216 | |||
| 217 | HRESULT ConditionGlobalParseFromXml( | ||
| 218 | __in BURN_CONDITION* pCondition, | ||
| 219 | __in IXMLDOMNode* pixnBundle | ||
| 220 | ) | ||
| 221 | { | ||
| 222 | HRESULT hr = S_OK; | ||
| 223 | IXMLDOMNode* pixnNode = NULL; | ||
| 224 | BSTR bstrExpression = NULL; | ||
| 225 | |||
| 226 | // select variable nodes | ||
| 227 | hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode); | ||
| 228 | if (S_FALSE == hr) | ||
| 229 | { | ||
| 230 | ExitFunction1(hr = S_OK); | ||
| 231 | } | ||
| 232 | ExitOnFailure(hr, "Failed to select condition node."); | ||
| 233 | |||
| 234 | // @Condition | ||
| 235 | hr = XmlGetText(pixnNode, &bstrExpression); | ||
| 236 | ExitOnFailure(hr, "Failed to get Condition inner text."); | ||
| 237 | |||
| 238 | hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0); | ||
| 239 | ExitOnFailure(hr, "Failed to copy condition string from BSTR"); | ||
| 240 | |||
| 241 | LExit: | ||
| 242 | ReleaseBSTR(bstrExpression); | ||
| 243 | ReleaseObject(pixnNode); | ||
| 244 | |||
| 245 | return hr; | ||
| 246 | } | ||
| 247 | |||
| 248 | |||
| 249 | // internal function definitions | ||
| 250 | |||
| 251 | static HRESULT ParseExpression( | ||
| 252 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 253 | __out BOOL* pf | ||
| 254 | ) | ||
| 255 | { | ||
| 256 | HRESULT hr = S_OK; | ||
| 257 | BOOL fFirst = FALSE; | ||
| 258 | BOOL fSecond = FALSE; | ||
| 259 | |||
| 260 | hr = ParseBooleanTerm(pContext, &fFirst); | ||
| 261 | ExitOnFailure(hr, "Failed to parse boolean-term."); | ||
| 262 | |||
| 263 | if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type) | ||
| 264 | { | ||
| 265 | hr = NextSymbol(pContext); | ||
| 266 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 267 | |||
| 268 | hr = ParseExpression(pContext, &fSecond); | ||
| 269 | ExitOnFailure(hr, "Failed to parse expression."); | ||
| 270 | |||
| 271 | *pf = fFirst || fSecond; | ||
| 272 | } | ||
| 273 | else | ||
| 274 | { | ||
| 275 | *pf = fFirst; | ||
| 276 | } | ||
| 277 | |||
| 278 | LExit: | ||
| 279 | return hr; | ||
| 280 | } | ||
| 281 | |||
| 282 | static HRESULT ParseBooleanTerm( | ||
| 283 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 284 | __out BOOL* pf | ||
| 285 | ) | ||
| 286 | { | ||
| 287 | HRESULT hr = S_OK; | ||
| 288 | BOOL fFirst = FALSE; | ||
| 289 | BOOL fSecond = FALSE; | ||
| 290 | |||
| 291 | hr = ParseBooleanFactor(pContext, &fFirst); | ||
| 292 | ExitOnFailure(hr, "Failed to parse boolean-factor."); | ||
| 293 | |||
| 294 | if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type) | ||
| 295 | { | ||
| 296 | hr = NextSymbol(pContext); | ||
| 297 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 298 | |||
| 299 | hr = ParseBooleanTerm(pContext, &fSecond); | ||
| 300 | ExitOnFailure(hr, "Failed to parse boolean-term."); | ||
| 301 | |||
| 302 | *pf = fFirst && fSecond; | ||
| 303 | } | ||
| 304 | else | ||
| 305 | { | ||
| 306 | *pf = fFirst; | ||
| 307 | } | ||
| 308 | |||
| 309 | LExit: | ||
| 310 | return hr; | ||
| 311 | } | ||
| 312 | |||
| 313 | static HRESULT ParseBooleanFactor( | ||
| 314 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 315 | __out BOOL* pf | ||
| 316 | ) | ||
| 317 | { | ||
| 318 | HRESULT hr = S_OK; | ||
| 319 | BOOL fNot = FALSE; | ||
| 320 | BOOL f = FALSE; | ||
| 321 | |||
| 322 | if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type) | ||
| 323 | { | ||
| 324 | hr = NextSymbol(pContext); | ||
| 325 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 326 | |||
| 327 | fNot = TRUE; | ||
| 328 | } | ||
| 329 | |||
| 330 | hr = ParseTerm(pContext, &f); | ||
| 331 | ExitOnFailure(hr, "Failed to parse term."); | ||
| 332 | |||
| 333 | *pf = fNot ? !f : f; | ||
| 334 | |||
| 335 | LExit: | ||
| 336 | return hr; | ||
| 337 | } | ||
| 338 | |||
| 339 | static HRESULT ParseTerm( | ||
| 340 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 341 | __out BOOL* pf | ||
| 342 | ) | ||
| 343 | { | ||
| 344 | HRESULT hr = S_OK; | ||
| 345 | BURN_VARIANT firstValue = { }; | ||
| 346 | BURN_VARIANT secondValue = { }; | ||
| 347 | |||
| 348 | if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type) | ||
| 349 | { | ||
| 350 | hr = NextSymbol(pContext); | ||
| 351 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 352 | |||
| 353 | hr = ParseExpression(pContext, pf); | ||
| 354 | ExitOnFailure(hr, "Failed to parse expression."); | ||
| 355 | |||
| 356 | hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN); | ||
| 357 | ExitOnFailure(hr, "Failed to expect right parenthesis."); | ||
| 358 | |||
| 359 | ExitFunction1(hr = S_OK); | ||
| 360 | } | ||
| 361 | |||
| 362 | hr = ParseValue(pContext, &firstValue); | ||
| 363 | ExitOnFailure(hr, "Failed to parse value."); | ||
| 364 | |||
| 365 | if (COMPARISON & pContext->NextSymbol.Type) | ||
| 366 | { | ||
| 367 | BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type; | ||
| 368 | |||
| 369 | hr = NextSymbol(pContext); | ||
| 370 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 371 | |||
| 372 | hr = ParseValue(pContext, &secondValue); | ||
| 373 | ExitOnFailure(hr, "Failed to parse value."); | ||
| 374 | |||
| 375 | hr = CompareValues(comparison, firstValue, secondValue, pf); | ||
| 376 | ExitOnFailure(hr, "Failed to compare value."); | ||
| 377 | } | ||
| 378 | else | ||
| 379 | { | ||
| 380 | LONGLONG llValue = 0; | ||
| 381 | LPWSTR sczValue = NULL; | ||
| 382 | DWORD64 qwValue = 0; | ||
| 383 | switch (firstValue.Type) | ||
| 384 | { | ||
| 385 | case BURN_VARIANT_TYPE_NONE: | ||
| 386 | *pf = FALSE; | ||
| 387 | break; | ||
| 388 | case BURN_VARIANT_TYPE_STRING: | ||
| 389 | hr = BVariantGetString(&firstValue, &sczValue); | ||
| 390 | if (SUCCEEDED(hr)) | ||
| 391 | { | ||
| 392 | *pf = sczValue && *sczValue; | ||
| 393 | } | ||
| 394 | StrSecureZeroFreeString(sczValue); | ||
| 395 | break; | ||
| 396 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 397 | hr = BVariantGetNumeric(&firstValue, &llValue); | ||
| 398 | if (SUCCEEDED(hr)) | ||
| 399 | { | ||
| 400 | *pf = 0 != llValue; | ||
| 401 | } | ||
| 402 | SecureZeroMemory(&llValue, sizeof(llValue)); | ||
| 403 | break; | ||
| 404 | case BURN_VARIANT_TYPE_VERSION: | ||
| 405 | hr = BVariantGetVersion(&firstValue, &qwValue); | ||
| 406 | if (SUCCEEDED(hr)) | ||
| 407 | { | ||
| 408 | *pf = 0 != qwValue; | ||
| 409 | } | ||
| 410 | SecureZeroMemory(&llValue, sizeof(qwValue)); | ||
| 411 | break; | ||
| 412 | default: | ||
| 413 | ExitFunction1(hr = E_UNEXPECTED); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | LExit: | ||
| 418 | BVariantUninitialize(&firstValue); | ||
| 419 | BVariantUninitialize(&secondValue); | ||
| 420 | return hr; | ||
| 421 | } | ||
| 422 | |||
| 423 | static HRESULT ParseValue( | ||
| 424 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 425 | __out BURN_VARIANT* pValue | ||
| 426 | ) | ||
| 427 | { | ||
| 428 | HRESULT hr = S_OK; | ||
| 429 | |||
| 430 | // Symbols don't encrypt their value, so can access the value directly. | ||
| 431 | switch (pContext->NextSymbol.Type) | ||
| 432 | { | ||
| 433 | case BURN_SYMBOL_TYPE_IDENTIFIER: | ||
| 434 | Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type); | ||
| 435 | |||
| 436 | // find variable | ||
| 437 | hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue); | ||
| 438 | if (E_NOTFOUND != hr) | ||
| 439 | { | ||
| 440 | ExitOnRootFailure(hr, "Failed to find variable."); | ||
| 441 | } | ||
| 442 | break; | ||
| 443 | |||
| 444 | case BURN_SYMBOL_TYPE_NUMBER: __fallthrough; | ||
| 445 | case BURN_SYMBOL_TYPE_LITERAL: __fallthrough; | ||
| 446 | case BURN_SYMBOL_TYPE_VERSION: | ||
| 447 | // steal value of symbol | ||
| 448 | memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT)); | ||
| 449 | memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT)); | ||
| 450 | break; | ||
| 451 | |||
| 452 | default: | ||
| 453 | pContext->fError = TRUE; | ||
| 454 | hr = E_INVALIDDATA; | ||
| 455 | ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); | ||
| 456 | } | ||
| 457 | |||
| 458 | // get next symbol | ||
| 459 | hr = NextSymbol(pContext); | ||
| 460 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 461 | |||
| 462 | LExit: | ||
| 463 | return hr; | ||
| 464 | } | ||
| 465 | |||
| 466 | // | ||
| 467 | // Expect - expects a symbol. | ||
| 468 | // | ||
| 469 | static HRESULT Expect( | ||
| 470 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
| 471 | __in BURN_SYMBOL_TYPE symbolType | ||
| 472 | ) | ||
| 473 | { | ||
| 474 | HRESULT hr = S_OK; | ||
| 475 | |||
| 476 | if (pContext->NextSymbol.Type != symbolType) | ||
| 477 | { | ||
| 478 | pContext->fError = TRUE; | ||
| 479 | hr = E_INVALIDDATA; | ||
| 480 | ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition); | ||
| 481 | } | ||
| 482 | |||
| 483 | hr = NextSymbol(pContext); | ||
| 484 | ExitOnFailure(hr, "Failed to read next symbol."); | ||
| 485 | |||
| 486 | LExit: | ||
| 487 | return hr; | ||
| 488 | } | ||
| 489 | |||
| 490 | // | ||
| 491 | // NextSymbol - finds the next symbol in an expression string. | ||
| 492 | // | ||
| 493 | static HRESULT NextSymbol( | ||
| 494 | __in BURN_CONDITION_PARSE_CONTEXT* pContext | ||
| 495 | ) | ||
| 496 | { | ||
| 497 | HRESULT hr = S_OK; | ||
| 498 | WORD charType = 0; | ||
| 499 | DWORD iPosition = 0; | ||
| 500 | DWORD n = 0; | ||
| 501 | |||
| 502 | // free existing symbol | ||
| 503 | BVariantUninitialize(&pContext->NextSymbol.Value); | ||
| 504 | memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL)); | ||
| 505 | |||
| 506 | // skip past blanks | ||
| 507 | while (L'\0' != pContext->wzRead[0]) | ||
| 508 | { | ||
| 509 | ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType); | ||
| 510 | if (0 == (C1_BLANK & charType)) | ||
| 511 | { | ||
| 512 | break; // no blank, done | ||
| 513 | } | ||
| 514 | ++pContext->wzRead; | ||
| 515 | } | ||
| 516 | iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition); | ||
| 517 | |||
| 518 | // read depending on first character type | ||
| 519 | switch (pContext->wzRead[0]) | ||
| 520 | { | ||
| 521 | case L'\0': | ||
| 522 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END; | ||
| 523 | break; | ||
| 524 | case L'~': | ||
| 525 | switch (pContext->wzRead[1]) | ||
| 526 | { | ||
| 527 | case L'=': | ||
| 528 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I; | ||
| 529 | n = 2; | ||
| 530 | break; | ||
| 531 | case L'>': | ||
| 532 | switch (pContext->wzRead[2]) | ||
| 533 | { | ||
| 534 | case '=': | ||
| 535 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I; | ||
| 536 | n = 3; | ||
| 537 | break; | ||
| 538 | case L'>': | ||
| 539 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I; | ||
| 540 | n = 3; | ||
| 541 | break; | ||
| 542 | case L'<': | ||
| 543 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I; | ||
| 544 | n = 3; | ||
| 545 | break; | ||
| 546 | default: | ||
| 547 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I; | ||
| 548 | n = 2; | ||
| 549 | } | ||
| 550 | break; | ||
| 551 | case L'<': | ||
| 552 | switch (pContext->wzRead[2]) | ||
| 553 | { | ||
| 554 | case '=': | ||
| 555 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I; | ||
| 556 | n = 3; | ||
| 557 | break; | ||
| 558 | case L'<': | ||
| 559 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I; | ||
| 560 | n = 3; | ||
| 561 | break; | ||
| 562 | case '>': | ||
| 563 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I; | ||
| 564 | n = 3; | ||
| 565 | break; | ||
| 566 | default: | ||
| 567 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I; | ||
| 568 | n = 2; | ||
| 569 | } | ||
| 570 | break; | ||
| 571 | default: | ||
| 572 | // error | ||
| 573 | pContext->fError = TRUE; | ||
| 574 | hr = E_INVALIDDATA; | ||
| 575 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition); | ||
| 576 | } | ||
| 577 | break; | ||
| 578 | case L'>': | ||
| 579 | switch (pContext->wzRead[1]) | ||
| 580 | { | ||
| 581 | case L'=': | ||
| 582 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE; | ||
| 583 | n = 2; | ||
| 584 | break; | ||
| 585 | case L'>': | ||
| 586 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ; | ||
| 587 | n = 2; | ||
| 588 | break; | ||
| 589 | case L'<': | ||
| 590 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND; | ||
| 591 | n = 2; | ||
| 592 | break; | ||
| 593 | default: | ||
| 594 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT; | ||
| 595 | n = 1; | ||
| 596 | } | ||
| 597 | break; | ||
| 598 | case L'<': | ||
| 599 | switch (pContext->wzRead[1]) | ||
| 600 | { | ||
| 601 | case L'=': | ||
| 602 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE; | ||
| 603 | n = 2; | ||
| 604 | break; | ||
| 605 | case L'<': | ||
| 606 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ; | ||
| 607 | n = 2; | ||
| 608 | break; | ||
| 609 | case L'>': | ||
| 610 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE; | ||
| 611 | n = 2; | ||
| 612 | break; | ||
| 613 | default: | ||
| 614 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT; | ||
| 615 | n = 1; | ||
| 616 | } | ||
| 617 | break; | ||
| 618 | case L'=': | ||
| 619 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ; | ||
| 620 | n = 1; | ||
| 621 | break; | ||
| 622 | case L'(': | ||
| 623 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN; | ||
| 624 | n = 1; | ||
| 625 | break; | ||
| 626 | case L')': | ||
| 627 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN; | ||
| 628 | n = 1; | ||
| 629 | break; | ||
| 630 | case L'"': // literal | ||
| 631 | do | ||
| 632 | { | ||
| 633 | ++n; | ||
| 634 | if (L'\0' == pContext->wzRead[n]) | ||
| 635 | { | ||
| 636 | // error | ||
| 637 | pContext->fError = TRUE; | ||
| 638 | hr = E_INVALIDDATA; | ||
| 639 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition); | ||
| 640 | } | ||
| 641 | } while (L'"' != pContext->wzRead[n]); | ||
| 642 | ++n; // terminating '"' | ||
| 643 | |||
| 644 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL; | ||
| 645 | hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2); | ||
| 646 | ExitOnFailure(hr, "Failed to set symbol value."); | ||
| 647 | break; | ||
| 648 | default: | ||
| 649 | if (C1_DIGIT & charType || L'-' == pContext->wzRead[0]) | ||
| 650 | { | ||
| 651 | do | ||
| 652 | { | ||
| 653 | ++n; | ||
| 654 | ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); | ||
| 655 | if (C1_ALPHA & charType || L'_' == pContext->wzRead[n]) | ||
| 656 | { | ||
| 657 | // error, identifier cannot start with a digit | ||
| 658 | pContext->fError = TRUE; | ||
| 659 | hr = E_INVALIDDATA; | ||
| 660 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition); | ||
| 661 | } | ||
| 662 | } while (C1_DIGIT & charType); | ||
| 663 | |||
| 664 | // number | ||
| 665 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER; | ||
| 666 | |||
| 667 | LONGLONG ll = 0; | ||
| 668 | hr = StrStringToInt64(pContext->wzRead, n, &ll); | ||
| 669 | if (FAILED(hr)) | ||
| 670 | { | ||
| 671 | pContext->fError = TRUE; | ||
| 672 | hr = E_INVALIDDATA; | ||
| 673 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition); | ||
| 674 | } | ||
| 675 | |||
| 676 | hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll); | ||
| 677 | ExitOnFailure(hr, "Failed to set symbol value."); | ||
| 678 | } | ||
| 679 | else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0]) | ||
| 680 | { | ||
| 681 | ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType); | ||
| 682 | if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType) | ||
| 683 | { | ||
| 684 | // version | ||
| 685 | DWORD cParts = 1; | ||
| 686 | for (;;) | ||
| 687 | { | ||
| 688 | ++n; | ||
| 689 | if (L'.' == pContext->wzRead[n]) | ||
| 690 | { | ||
| 691 | ++cParts; | ||
| 692 | if (4 < cParts) | ||
| 693 | { | ||
| 694 | // error, too many parts in version | ||
| 695 | pContext->fError = TRUE; | ||
| 696 | hr = E_INVALIDDATA; | ||
| 697 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | else | ||
| 701 | { | ||
| 702 | ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); | ||
| 703 | if (C1_DIGIT != (C1_DIGIT & charType)) | ||
| 704 | { | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | } | ||
| 709 | |||
| 710 | // Symbols don't encrypt their value, so can access the value directly. | ||
| 711 | hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue); | ||
| 712 | if (FAILED(hr)) | ||
| 713 | { | ||
| 714 | pContext->fError = TRUE; | ||
| 715 | hr = E_INVALIDDATA; | ||
| 716 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition); | ||
| 717 | } | ||
| 718 | |||
| 719 | pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION; | ||
| 720 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION; | ||
| 721 | } | ||
| 722 | else | ||
| 723 | { | ||
| 724 | do | ||
| 725 | { | ||
| 726 | ++n; | ||
| 727 | ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType); | ||
| 728 | } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]); | ||
| 729 | |||
| 730 | if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2)) | ||
| 731 | { | ||
| 732 | // OR | ||
| 733 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR; | ||
| 734 | } | ||
| 735 | else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3)) | ||
| 736 | { | ||
| 737 | // AND | ||
| 738 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND; | ||
| 739 | } | ||
| 740 | else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3)) | ||
| 741 | { | ||
| 742 | // NOT | ||
| 743 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT; | ||
| 744 | } | ||
| 745 | else | ||
| 746 | { | ||
| 747 | // identifier | ||
| 748 | pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER; | ||
| 749 | hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n); | ||
| 750 | ExitOnFailure(hr, "Failed to set symbol value."); | ||
| 751 | } | ||
| 752 | } | ||
| 753 | } | ||
| 754 | else | ||
| 755 | { | ||
| 756 | // error, unexpected character | ||
| 757 | pContext->fError = TRUE; | ||
| 758 | hr = E_INVALIDDATA; | ||
| 759 | ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition); | ||
| 760 | } | ||
| 761 | } | ||
| 762 | pContext->NextSymbol.iPosition = iPosition; | ||
| 763 | pContext->wzRead += n; | ||
| 764 | |||
| 765 | LExit: | ||
| 766 | return hr; | ||
| 767 | } | ||
| 768 | |||
| 769 | // | ||
| 770 | // CompareValues - compares two variant values using a given comparison. | ||
| 771 | // | ||
| 772 | static HRESULT CompareValues( | ||
| 773 | __in BURN_SYMBOL_TYPE comparison, | ||
| 774 | __in BURN_VARIANT leftOperand, | ||
| 775 | __in BURN_VARIANT rightOperand, | ||
| 776 | __out BOOL* pfResult | ||
| 777 | ) | ||
| 778 | { | ||
| 779 | HRESULT hr = S_OK; | ||
| 780 | LONGLONG llLeft = 0; | ||
| 781 | DWORD64 qwLeft = 0; | ||
| 782 | LPWSTR sczLeft = NULL; | ||
| 783 | LONGLONG llRight = 0; | ||
| 784 | DWORD64 qwRight = 0; | ||
| 785 | LPWSTR sczRight = NULL; | ||
| 786 | |||
| 787 | // get values to compare based on type | ||
| 788 | if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) | ||
| 789 | { | ||
| 790 | hr = BVariantGetString(&leftOperand, &sczLeft); | ||
| 791 | ExitOnFailure(hr, "Failed to get the left string"); | ||
| 792 | hr = BVariantGetString(&rightOperand, &sczRight); | ||
| 793 | ExitOnFailure(hr, "Failed to get the right string"); | ||
| 794 | hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult); | ||
| 795 | } | ||
| 796 | else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) | ||
| 797 | { | ||
| 798 | hr = BVariantGetNumeric(&leftOperand, &llLeft); | ||
| 799 | ExitOnFailure(hr, "Failed to get the left numeric"); | ||
| 800 | hr = BVariantGetNumeric(&rightOperand, &llRight); | ||
| 801 | ExitOnFailure(hr, "Failed to get the right numeric"); | ||
| 802 | hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); | ||
| 803 | } | ||
| 804 | else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) | ||
| 805 | { | ||
| 806 | hr = BVariantGetVersion(&leftOperand, &qwLeft); | ||
| 807 | ExitOnFailure(hr, "Failed to get the left version"); | ||
| 808 | hr = BVariantGetVersion(&rightOperand, &qwRight); | ||
| 809 | ExitOnFailure(hr, "Failed to get the right version"); | ||
| 810 | hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); | ||
| 811 | } | ||
| 812 | else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) | ||
| 813 | { | ||
| 814 | hr = BVariantGetVersion(&leftOperand, &qwLeft); | ||
| 815 | ExitOnFailure(hr, "Failed to get the left version"); | ||
| 816 | hr = BVariantGetVersion(&rightOperand, &qwRight); | ||
| 817 | if (FAILED(hr)) | ||
| 818 | { | ||
| 819 | if (DISP_E_TYPEMISMATCH != hr) | ||
| 820 | { | ||
| 821 | ExitOnFailure(hr, "Failed to get the right version"); | ||
| 822 | } | ||
| 823 | *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); | ||
| 824 | hr = S_OK; | ||
| 825 | } | ||
| 826 | else | ||
| 827 | { | ||
| 828 | hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); | ||
| 829 | } | ||
| 830 | } | ||
| 831 | else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type) | ||
| 832 | { | ||
| 833 | hr = BVariantGetVersion(&rightOperand, &qwRight); | ||
| 834 | ExitOnFailure(hr, "Failed to get the right version"); | ||
| 835 | hr = BVariantGetVersion(&leftOperand, &qwLeft); | ||
| 836 | if (FAILED(hr)) | ||
| 837 | { | ||
| 838 | if (DISP_E_TYPEMISMATCH != hr) | ||
| 839 | { | ||
| 840 | ExitOnFailure(hr, "Failed to get the left version"); | ||
| 841 | } | ||
| 842 | *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); | ||
| 843 | hr = S_OK; | ||
| 844 | } | ||
| 845 | else | ||
| 846 | { | ||
| 847 | hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult); | ||
| 848 | } | ||
| 849 | } | ||
| 850 | else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type) | ||
| 851 | { | ||
| 852 | hr = BVariantGetNumeric(&leftOperand, &llLeft); | ||
| 853 | ExitOnFailure(hr, "Failed to get the left numeric"); | ||
| 854 | hr = BVariantGetNumeric(&rightOperand, &llRight); | ||
| 855 | if (FAILED(hr)) | ||
| 856 | { | ||
| 857 | if (DISP_E_TYPEMISMATCH != hr) | ||
| 858 | { | ||
| 859 | ExitOnFailure(hr, "Failed to get the right numeric"); | ||
| 860 | } | ||
| 861 | *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); | ||
| 862 | hr = S_OK; | ||
| 863 | } | ||
| 864 | else | ||
| 865 | { | ||
| 866 | hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); | ||
| 867 | } | ||
| 868 | } | ||
| 869 | else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type) | ||
| 870 | { | ||
| 871 | hr = BVariantGetNumeric(&rightOperand, &llRight); | ||
| 872 | ExitOnFailure(hr, "Failed to get the right numeric"); | ||
| 873 | hr = BVariantGetNumeric(&leftOperand, &llLeft); | ||
| 874 | if (FAILED(hr)) | ||
| 875 | { | ||
| 876 | if (DISP_E_TYPEMISMATCH != hr) | ||
| 877 | { | ||
| 878 | ExitOnFailure(hr, "Failed to get the left numeric"); | ||
| 879 | } | ||
| 880 | *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); | ||
| 881 | hr = S_OK; | ||
| 882 | } | ||
| 883 | else | ||
| 884 | { | ||
| 885 | hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult); | ||
| 886 | } | ||
| 887 | } | ||
| 888 | else | ||
| 889 | { | ||
| 890 | // not a combination that can be compared | ||
| 891 | *pfResult = (BURN_SYMBOL_TYPE_NE == comparison); | ||
| 892 | } | ||
| 893 | |||
| 894 | LExit: | ||
| 895 | SecureZeroMemory(&qwLeft, sizeof(DWORD64)); | ||
| 896 | SecureZeroMemory(&llLeft, sizeof(LONGLONG)); | ||
| 897 | StrSecureZeroFreeString(sczLeft); | ||
| 898 | SecureZeroMemory(&qwRight, sizeof(DWORD64)); | ||
| 899 | SecureZeroMemory(&llRight, sizeof(LONGLONG)); | ||
| 900 | StrSecureZeroFreeString(sczRight); | ||
| 901 | |||
| 902 | return hr; | ||
| 903 | } | ||
| 904 | |||
| 905 | // | ||
| 906 | // CompareStringValues - compares two string values using a given comparison. | ||
| 907 | // | ||
| 908 | static HRESULT CompareStringValues( | ||
| 909 | __in BURN_SYMBOL_TYPE comparison, | ||
| 910 | __in_z LPCWSTR wzLeftOperand, | ||
| 911 | __in_z LPCWSTR wzRightOperand, | ||
| 912 | __out BOOL* pfResult | ||
| 913 | ) | ||
| 914 | { | ||
| 915 | HRESULT hr = S_OK; | ||
| 916 | DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0; | ||
| 917 | int cchLeft = lstrlenW(wzLeftOperand); | ||
| 918 | int cchRight = lstrlenW(wzRightOperand); | ||
| 919 | |||
| 920 | switch (comparison) | ||
| 921 | { | ||
| 922 | case BURN_SYMBOL_TYPE_LT: | ||
| 923 | case BURN_SYMBOL_TYPE_GT: | ||
| 924 | case BURN_SYMBOL_TYPE_LE: | ||
| 925 | case BURN_SYMBOL_TYPE_GE: | ||
| 926 | case BURN_SYMBOL_TYPE_EQ: | ||
| 927 | case BURN_SYMBOL_TYPE_NE: | ||
| 928 | case BURN_SYMBOL_TYPE_LT_I: | ||
| 929 | case BURN_SYMBOL_TYPE_GT_I: | ||
| 930 | case BURN_SYMBOL_TYPE_LE_I: | ||
| 931 | case BURN_SYMBOL_TYPE_GE_I: | ||
| 932 | case BURN_SYMBOL_TYPE_EQ_I: | ||
| 933 | case BURN_SYMBOL_TYPE_NE_I: | ||
| 934 | { | ||
| 935 | int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight); | ||
| 936 | hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult); | ||
| 937 | } | ||
| 938 | break; | ||
| 939 | case BURN_SYMBOL_TYPE_BAND: | ||
| 940 | case BURN_SYMBOL_TYPE_BAND_I: | ||
| 941 | // test if left string contains right string | ||
| 942 | for (int i = 0; (i + cchRight) <= cchLeft; ++i) | ||
| 943 | { | ||
| 944 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight)) | ||
| 945 | { | ||
| 946 | *pfResult = TRUE; | ||
| 947 | ExitFunction(); | ||
| 948 | } | ||
| 949 | } | ||
| 950 | *pfResult = FALSE; | ||
| 951 | break; | ||
| 952 | case BURN_SYMBOL_TYPE_HIEQ: | ||
| 953 | case BURN_SYMBOL_TYPE_HIEQ_I: | ||
| 954 | // test if left string starts with right string | ||
| 955 | *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight); | ||
| 956 | break; | ||
| 957 | case BURN_SYMBOL_TYPE_LOEQ: | ||
| 958 | case BURN_SYMBOL_TYPE_LOEQ_I: | ||
| 959 | // test if left string ends with right string | ||
| 960 | *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight); | ||
| 961 | break; | ||
| 962 | default: | ||
| 963 | ExitFunction1(hr = E_INVALIDARG); | ||
| 964 | } | ||
| 965 | |||
| 966 | LExit: | ||
| 967 | return hr; | ||
| 968 | } | ||
| 969 | |||
| 970 | // | ||
| 971 | // CompareIntegerValues - compares two integer values using a given comparison. | ||
| 972 | // | ||
| 973 | static HRESULT CompareIntegerValues( | ||
| 974 | __in BURN_SYMBOL_TYPE comparison, | ||
| 975 | __in LONGLONG llLeftOperand, | ||
| 976 | __in LONGLONG llRightOperand, | ||
| 977 | __out BOOL* pfResult | ||
| 978 | ) | ||
| 979 | { | ||
| 980 | HRESULT hr = S_OK; | ||
| 981 | |||
| 982 | switch (comparison) | ||
| 983 | { | ||
| 984 | case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break; | ||
| 985 | case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break; | ||
| 986 | case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break; | ||
| 987 | case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break; | ||
| 988 | case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break; | ||
| 989 | case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break; | ||
| 990 | case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break; | ||
| 991 | case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break; | ||
| 992 | case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break; | ||
| 993 | default: | ||
| 994 | ExitFunction1(hr = E_INVALIDARG); | ||
| 995 | } | ||
| 996 | |||
| 997 | LExit: | ||
| 998 | return hr; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | // | ||
| 1002 | // CompareVersionValues - compares two quad-word version values using a given comparison. | ||
| 1003 | // | ||
| 1004 | static HRESULT CompareVersionValues( | ||
| 1005 | __in BURN_SYMBOL_TYPE comparison, | ||
| 1006 | __in DWORD64 qwLeftOperand, | ||
| 1007 | __in DWORD64 qwRightOperand, | ||
| 1008 | __out BOOL* pfResult | ||
| 1009 | ) | ||
| 1010 | { | ||
| 1011 | HRESULT hr = S_OK; | ||
| 1012 | |||
| 1013 | switch (comparison) | ||
| 1014 | { | ||
| 1015 | case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break; | ||
| 1016 | case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break; | ||
| 1017 | case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break; | ||
| 1018 | case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break; | ||
| 1019 | case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break; | ||
| 1020 | case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break; | ||
| 1021 | case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break; | ||
| 1022 | case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break; | ||
| 1023 | case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break; | ||
| 1024 | default: | ||
| 1025 | ExitFunction1(hr = E_INVALIDARG); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | LExit: | ||
| 1029 | return hr; | ||
| 1030 | } | ||
diff --git a/src/engine/condition.h b/src/engine/condition.h new file mode 100644 index 00000000..91627f3c --- /dev/null +++ b/src/engine/condition.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | typedef struct _BURN_CONDITION | ||
| 11 | { | ||
| 12 | // The is an expression a condition string to fire the built-in "need newer OS" message | ||
| 13 | LPWSTR sczConditionString; | ||
| 14 | } BURN_CONDITION; | ||
| 15 | |||
| 16 | |||
| 17 | // function declarations | ||
| 18 | |||
| 19 | HRESULT ConditionEvaluate( | ||
| 20 | __in BURN_VARIABLES* pVariables, | ||
| 21 | __in_z LPCWSTR wzCondition, | ||
| 22 | __out BOOL* pf | ||
| 23 | ); | ||
| 24 | HRESULT ConditionGlobalCheck( | ||
| 25 | __in BURN_VARIABLES* pVariables, | ||
| 26 | __in BURN_CONDITION* pBlock, | ||
| 27 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 28 | __in_z LPCWSTR wzBundleName, | ||
| 29 | __out DWORD *pdwExitCode, | ||
| 30 | __out BOOL *pfContinueExecution | ||
| 31 | ); | ||
| 32 | HRESULT ConditionGlobalParseFromXml( | ||
| 33 | __in BURN_CONDITION* pBlock, | ||
| 34 | __in IXMLDOMNode* pixnBundle | ||
| 35 | ); | ||
| 36 | |||
| 37 | #if defined(__cplusplus) | ||
| 38 | } | ||
| 39 | #endif | ||
diff --git a/src/engine/container.cpp b/src/engine/container.cpp new file mode 100644 index 00000000..ada9025b --- /dev/null +++ b/src/engine/container.cpp | |||
| @@ -0,0 +1,386 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | static HRESULT GetAttachedContainerInfo( | ||
| 9 | __in HANDLE hFile, | ||
| 10 | __in DWORD iContainerIndex, | ||
| 11 | __out DWORD* pdwFormat, | ||
| 12 | __out DWORD64* pqwOffset, | ||
| 13 | __out DWORD64* pqwSize | ||
| 14 | ); | ||
| 15 | |||
| 16 | |||
| 17 | // function definitions | ||
| 18 | |||
| 19 | extern "C" HRESULT ContainersParseFromXml( | ||
| 20 | __in BURN_SECTION* pSection, | ||
| 21 | __in BURN_CONTAINERS* pContainers, | ||
| 22 | __in IXMLDOMNode* pixnBundle | ||
| 23 | ) | ||
| 24 | { | ||
| 25 | HRESULT hr = S_OK; | ||
| 26 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 27 | IXMLDOMNode* pixnNode = NULL; | ||
| 28 | DWORD cNodes = 0; | ||
| 29 | LPWSTR scz = NULL; | ||
| 30 | |||
| 31 | // select container nodes | ||
| 32 | hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes); | ||
| 33 | ExitOnFailure(hr, "Failed to select container nodes."); | ||
| 34 | |||
| 35 | // get container node count | ||
| 36 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 37 | ExitOnFailure(hr, "Failed to get container node count."); | ||
| 38 | |||
| 39 | if (!cNodes) | ||
| 40 | { | ||
| 41 | ExitFunction(); | ||
| 42 | } | ||
| 43 | |||
| 44 | // allocate memory for searches | ||
| 45 | pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE); | ||
| 46 | ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs."); | ||
| 47 | |||
| 48 | pContainers->cContainers = cNodes; | ||
| 49 | |||
| 50 | // parse search elements | ||
| 51 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 52 | { | ||
| 53 | BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; | ||
| 54 | |||
| 55 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 56 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 57 | |||
| 58 | // TODO: Read type from manifest. Today only CABINET is supported. | ||
| 59 | pContainer->type = BURN_CONTAINER_TYPE_CABINET; | ||
| 60 | |||
| 61 | // @Id | ||
| 62 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId); | ||
| 63 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 64 | |||
| 65 | // @Primary | ||
| 66 | hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary); | ||
| 67 | if (E_NOTFOUND != hr) | ||
| 68 | { | ||
| 69 | ExitOnFailure(hr, "Failed to get @Primary."); | ||
| 70 | } | ||
| 71 | |||
| 72 | // @Attached | ||
| 73 | hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached); | ||
| 74 | if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached | ||
| 75 | { | ||
| 76 | ExitOnFailure(hr, "Failed to get @Attached."); | ||
| 77 | } | ||
| 78 | |||
| 79 | // @AttachedIndex | ||
| 80 | hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex); | ||
| 81 | if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index | ||
| 82 | { | ||
| 83 | ExitOnFailure(hr, "Failed to get @AttachedIndex."); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Attached containers are always found attached to the current process, so use the current proccess's | ||
| 87 | // name instead of what may be in the manifest. | ||
| 88 | if (pContainer->fAttached) | ||
| 89 | { | ||
| 90 | hr = PathForCurrentProcess(&scz, NULL); | ||
| 91 | ExitOnFailure(hr, "Failed to get path to current process for attached container."); | ||
| 92 | |||
| 93 | LPCWSTR wzFileName = PathFile(scz); | ||
| 94 | |||
| 95 | hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0); | ||
| 96 | ExitOnFailure(hr, "Failed to set attached container file path."); | ||
| 97 | } | ||
| 98 | else | ||
| 99 | { | ||
| 100 | // @FilePath | ||
| 101 | hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath); | ||
| 102 | if (E_NOTFOUND != hr) | ||
| 103 | { | ||
| 104 | ExitOnFailure(hr, "Failed to get @FilePath."); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // The source path starts as the file path. | ||
| 109 | hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0); | ||
| 110 | ExitOnFailure(hr, "Failed to copy @FilePath"); | ||
| 111 | |||
| 112 | // @DownloadUrl | ||
| 113 | hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl); | ||
| 114 | if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url | ||
| 115 | { | ||
| 116 | ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided."); | ||
| 117 | } | ||
| 118 | |||
| 119 | // @Hash | ||
| 120 | hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash); | ||
| 121 | if (SUCCEEDED(hr)) | ||
| 122 | { | ||
| 123 | hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash); | ||
| 124 | ExitOnFailure(hr, "Failed to hex decode the Container/@Hash."); | ||
| 125 | } | ||
| 126 | else if (E_NOTFOUND != hr) | ||
| 127 | { | ||
| 128 | ExitOnFailure(hr, "Failed to get @Hash."); | ||
| 129 | } | ||
| 130 | |||
| 131 | // If the container is attached, make sure the information in the section matches what the | ||
| 132 | // manifest contained and get the offset to the container. | ||
| 133 | if (pContainer->fAttached) | ||
| 134 | { | ||
| 135 | hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached); | ||
| 136 | ExitOnFailure(hr, "Failed to get attached container information."); | ||
| 137 | } | ||
| 138 | |||
| 139 | // prepare next iteration | ||
| 140 | ReleaseNullObject(pixnNode); | ||
| 141 | } | ||
| 142 | |||
| 143 | hr = S_OK; | ||
| 144 | |||
| 145 | LExit: | ||
| 146 | ReleaseObject(pixnNodes); | ||
| 147 | ReleaseObject(pixnNode); | ||
| 148 | ReleaseStr(scz); | ||
| 149 | |||
| 150 | return hr; | ||
| 151 | } | ||
| 152 | |||
| 153 | extern "C" void ContainersUninitialize( | ||
| 154 | __in BURN_CONTAINERS* pContainers | ||
| 155 | ) | ||
| 156 | { | ||
| 157 | if (pContainers->rgContainers) | ||
| 158 | { | ||
| 159 | for (DWORD i = 0; i < pContainers->cContainers; ++i) | ||
| 160 | { | ||
| 161 | BURN_CONTAINER* pContainer = &pContainers->rgContainers[i]; | ||
| 162 | |||
| 163 | ReleaseStr(pContainer->sczId); | ||
| 164 | ReleaseStr(pContainer->sczHash); | ||
| 165 | ReleaseStr(pContainer->sczSourcePath); | ||
| 166 | ReleaseStr(pContainer->sczFilePath); | ||
| 167 | ReleaseMem(pContainer->pbHash); | ||
| 168 | ReleaseStr(pContainer->downloadSource.sczUrl); | ||
| 169 | ReleaseStr(pContainer->downloadSource.sczUser); | ||
| 170 | ReleaseStr(pContainer->downloadSource.sczPassword); | ||
| 171 | } | ||
| 172 | MemFree(pContainers->rgContainers); | ||
| 173 | } | ||
| 174 | |||
| 175 | // clear struct | ||
| 176 | memset(pContainers, 0, sizeof(BURN_CONTAINERS)); | ||
| 177 | } | ||
| 178 | |||
| 179 | extern "C" HRESULT ContainerOpenUX( | ||
| 180 | __in BURN_SECTION* pSection, | ||
| 181 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 182 | ) | ||
| 183 | { | ||
| 184 | HRESULT hr = S_OK; | ||
| 185 | BURN_CONTAINER container = { }; | ||
| 186 | LPWSTR sczExecutablePath = NULL; | ||
| 187 | |||
| 188 | // open attached container | ||
| 189 | container.type = BURN_CONTAINER_TYPE_CABINET; | ||
| 190 | container.fPrimary = TRUE; | ||
| 191 | container.fAttached = TRUE; | ||
| 192 | container.dwAttachedIndex = 0; | ||
| 193 | |||
| 194 | hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached); | ||
| 195 | ExitOnFailure(hr, "Failed to get container information for UX container."); | ||
| 196 | |||
| 197 | AssertSz(container.fActuallyAttached, "The BA container must always be found attached."); | ||
| 198 | |||
| 199 | hr = PathForCurrentProcess(&sczExecutablePath, NULL); | ||
| 200 | ExitOnFailure(hr, "Failed to get path for executing module."); | ||
| 201 | |||
| 202 | hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath); | ||
| 203 | ExitOnFailure(hr, "Failed to open attached container."); | ||
| 204 | |||
| 205 | LExit: | ||
| 206 | ReleaseStr(sczExecutablePath); | ||
| 207 | |||
| 208 | return hr; | ||
| 209 | } | ||
| 210 | |||
| 211 | extern "C" HRESULT ContainerOpen( | ||
| 212 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 213 | __in BURN_CONTAINER* pContainer, | ||
| 214 | __in HANDLE hContainerFile, | ||
| 215 | __in_z LPCWSTR wzFilePath | ||
| 216 | ) | ||
| 217 | { | ||
| 218 | HRESULT hr = S_OK; | ||
| 219 | LARGE_INTEGER li = { }; | ||
| 220 | |||
| 221 | // initialize context | ||
| 222 | pContext->type = pContainer->type; | ||
| 223 | pContext->qwSize = pContainer->qwFileSize; | ||
| 224 | pContext->qwOffset = pContainer->qwAttachedOffset; | ||
| 225 | |||
| 226 | // If the handle to the container is not open already, open container file | ||
| 227 | if (INVALID_HANDLE_VALUE == hContainerFile) | ||
| 228 | { | ||
| 229 | pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
| 230 | ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath); | ||
| 231 | } | ||
| 232 | else // use the container file handle. | ||
| 233 | { | ||
| 234 | if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
| 235 | { | ||
| 236 | ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | // If it is a container attached to an executable, seek to the container offset. | ||
| 241 | if (pContainer->fAttached) | ||
| 242 | { | ||
| 243 | li.QuadPart = (LONGLONG)pContext->qwOffset; | ||
| 244 | } | ||
| 245 | |||
| 246 | if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN)) | ||
| 247 | { | ||
| 248 | ExitWithLastError(hr, "Failed to move file pointer to container offset."); | ||
| 249 | } | ||
| 250 | |||
| 251 | // open the archive | ||
| 252 | switch (pContext->type) | ||
| 253 | { | ||
| 254 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 255 | hr = CabExtractOpen(pContext, wzFilePath); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | ExitOnFailure(hr, "Failed to open container."); | ||
| 259 | |||
| 260 | LExit: | ||
| 261 | return hr; | ||
| 262 | } | ||
| 263 | |||
| 264 | extern "C" HRESULT ContainerNextStream( | ||
| 265 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 266 | __inout_z LPWSTR* psczStreamName | ||
| 267 | ) | ||
| 268 | { | ||
| 269 | HRESULT hr = S_OK; | ||
| 270 | |||
| 271 | switch (pContext->type) | ||
| 272 | { | ||
| 273 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 274 | hr = CabExtractNextStream(pContext, psczStreamName); | ||
| 275 | break; | ||
| 276 | } | ||
| 277 | |||
| 278 | //LExit: | ||
| 279 | return hr; | ||
| 280 | } | ||
| 281 | |||
| 282 | extern "C" HRESULT ContainerStreamToFile( | ||
| 283 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 284 | __in_z LPCWSTR wzFileName | ||
| 285 | ) | ||
| 286 | { | ||
| 287 | HRESULT hr = S_OK; | ||
| 288 | |||
| 289 | switch (pContext->type) | ||
| 290 | { | ||
| 291 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 292 | hr = CabExtractStreamToFile(pContext, wzFileName); | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | |||
| 296 | //LExit: | ||
| 297 | return hr; | ||
| 298 | } | ||
| 299 | |||
| 300 | extern "C" HRESULT ContainerStreamToBuffer( | ||
| 301 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 302 | __out BYTE** ppbBuffer, | ||
| 303 | __out SIZE_T* pcbBuffer | ||
| 304 | ) | ||
| 305 | { | ||
| 306 | HRESULT hr = S_OK; | ||
| 307 | |||
| 308 | switch (pContext->type) | ||
| 309 | { | ||
| 310 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 311 | hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer); | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | |||
| 315 | //LExit: | ||
| 316 | return hr; | ||
| 317 | } | ||
| 318 | |||
| 319 | extern "C" HRESULT ContainerSkipStream( | ||
| 320 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 321 | ) | ||
| 322 | { | ||
| 323 | HRESULT hr = S_OK; | ||
| 324 | |||
| 325 | switch (pContext->type) | ||
| 326 | { | ||
| 327 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 328 | hr = CabExtractSkipStream(pContext); | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | |||
| 332 | //LExit: | ||
| 333 | return hr; | ||
| 334 | } | ||
| 335 | |||
| 336 | extern "C" HRESULT ContainerClose( | ||
| 337 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 338 | ) | ||
| 339 | { | ||
| 340 | HRESULT hr = S_OK; | ||
| 341 | |||
| 342 | // close container | ||
| 343 | switch (pContext->type) | ||
| 344 | { | ||
| 345 | case BURN_CONTAINER_TYPE_CABINET: | ||
| 346 | hr = CabExtractClose(pContext); | ||
| 347 | ExitOnFailure(hr, "Failed to close cabinet."); | ||
| 348 | break; | ||
| 349 | } | ||
| 350 | |||
| 351 | LExit: | ||
| 352 | ReleaseFile(pContext->hFile); | ||
| 353 | |||
| 354 | if (SUCCEEDED(hr)) | ||
| 355 | { | ||
| 356 | memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT)); | ||
| 357 | } | ||
| 358 | |||
| 359 | return hr; | ||
| 360 | } | ||
| 361 | |||
| 362 | extern "C" HRESULT ContainerFindById( | ||
| 363 | __in BURN_CONTAINERS* pContainers, | ||
| 364 | __in_z LPCWSTR wzId, | ||
| 365 | __out BURN_CONTAINER** ppContainer | ||
| 366 | ) | ||
| 367 | { | ||
| 368 | HRESULT hr = S_OK; | ||
| 369 | BURN_CONTAINER* pContainer = NULL; | ||
| 370 | |||
| 371 | for (DWORD i = 0; i < pContainers->cContainers; ++i) | ||
| 372 | { | ||
| 373 | pContainer = &pContainers->rgContainers[i]; | ||
| 374 | |||
| 375 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1)) | ||
| 376 | { | ||
| 377 | *ppContainer = pContainer; | ||
| 378 | ExitFunction1(hr = S_OK); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | hr = E_NOTFOUND; | ||
| 383 | |||
| 384 | LExit: | ||
| 385 | return hr; | ||
| 386 | } | ||
diff --git a/src/engine/container.h b/src/engine/container.h new file mode 100644 index 00000000..2ca3d7ad --- /dev/null +++ b/src/engine/container.h | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // typedefs | ||
| 11 | |||
| 12 | //typedef HRESULT (*PFN_EXTRACTOPEN)( | ||
| 13 | // __in HANDLE hFile, | ||
| 14 | // __in DWORD64 qwOffset, | ||
| 15 | // __in DWORD64 qwSize, | ||
| 16 | // __out void** ppCookie | ||
| 17 | // ); | ||
| 18 | //typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)( | ||
| 19 | // __in void* pCookie, | ||
| 20 | // __inout_z LPWSTR* psczStreamName | ||
| 21 | // ); | ||
| 22 | //typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)( | ||
| 23 | // __in void* pCookie, | ||
| 24 | // __in_z LPCWSTR wzFileName | ||
| 25 | // ); | ||
| 26 | //typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)( | ||
| 27 | // __in void* pCookie, | ||
| 28 | // __out BYTE** ppbBuffer, | ||
| 29 | // __out SIZE_T* pcbBuffer | ||
| 30 | // ); | ||
| 31 | //typedef HRESULT (*PFN_EXTRACTCLOSE)( | ||
| 32 | // __in void* pCookie | ||
| 33 | // ); | ||
| 34 | |||
| 35 | |||
| 36 | // constants | ||
| 37 | |||
| 38 | enum BURN_CONTAINER_TYPE | ||
| 39 | { | ||
| 40 | BURN_CONTAINER_TYPE_NONE, | ||
| 41 | BURN_CONTAINER_TYPE_CABINET, | ||
| 42 | BURN_CONTAINER_TYPE_SEVENZIP, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum BURN_CAB_OPERATION | ||
| 46 | { | ||
| 47 | BURN_CAB_OPERATION_NONE, | ||
| 48 | BURN_CAB_OPERATION_NEXT_STREAM, | ||
| 49 | BURN_CAB_OPERATION_STREAM_TO_FILE, | ||
| 50 | BURN_CAB_OPERATION_STREAM_TO_BUFFER, | ||
| 51 | BURN_CAB_OPERATION_SKIP_STREAM, | ||
| 52 | BURN_CAB_OPERATION_CLOSE, | ||
| 53 | }; | ||
| 54 | |||
| 55 | |||
| 56 | // structs | ||
| 57 | |||
| 58 | typedef struct _BURN_CONTAINER | ||
| 59 | { | ||
| 60 | LPWSTR sczId; | ||
| 61 | BURN_CONTAINER_TYPE type; | ||
| 62 | BOOL fPrimary; | ||
| 63 | BOOL fAttached; | ||
| 64 | DWORD dwAttachedIndex; | ||
| 65 | DWORD64 qwFileSize; | ||
| 66 | LPWSTR sczHash; | ||
| 67 | LPWSTR sczFilePath; // relative path to container. | ||
| 68 | LPWSTR sczSourcePath; | ||
| 69 | DOWNLOAD_SOURCE downloadSource; | ||
| 70 | |||
| 71 | BYTE* pbHash; | ||
| 72 | DWORD cbHash; | ||
| 73 | DWORD64 qwAttachedOffset; | ||
| 74 | BOOL fActuallyAttached; // indicates whether an attached container is attached or missing. | ||
| 75 | |||
| 76 | //LPWSTR* rgsczPayloads; | ||
| 77 | //DWORD cPayloads; | ||
| 78 | } BURN_CONTAINER; | ||
| 79 | |||
| 80 | typedef struct _BURN_CONTAINERS | ||
| 81 | { | ||
| 82 | BURN_CONTAINER* rgContainers; | ||
| 83 | DWORD cContainers; | ||
| 84 | } BURN_CONTAINERS; | ||
| 85 | |||
| 86 | typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER | ||
| 87 | { | ||
| 88 | HANDLE hFile; | ||
| 89 | LARGE_INTEGER liPosition; | ||
| 90 | } BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER; | ||
| 91 | |||
| 92 | typedef struct _BURN_CONTAINER_CONTEXT_CABINET | ||
| 93 | { | ||
| 94 | LPWSTR sczFile; | ||
| 95 | |||
| 96 | HANDLE hThread; | ||
| 97 | HANDLE hBeginOperationEvent; | ||
| 98 | HANDLE hOperationCompleteEvent; | ||
| 99 | |||
| 100 | BURN_CAB_OPERATION operation; | ||
| 101 | HRESULT hrError; | ||
| 102 | |||
| 103 | LPWSTR* psczStreamName; | ||
| 104 | LPCWSTR wzTargetFile; | ||
| 105 | HANDLE hTargetFile; | ||
| 106 | BYTE* pbTargetBuffer; | ||
| 107 | DWORD cbTargetBuffer; | ||
| 108 | DWORD iTargetBuffer; | ||
| 109 | |||
| 110 | BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers; | ||
| 111 | DWORD cVirtualFilePointers; | ||
| 112 | } BURN_CONTAINER_CONTEXT_CABINET; | ||
| 113 | |||
| 114 | typedef struct _BURN_CONTAINER_CONTEXT | ||
| 115 | { | ||
| 116 | HANDLE hFile; | ||
| 117 | DWORD64 qwOffset; | ||
| 118 | DWORD64 qwSize; | ||
| 119 | |||
| 120 | //PFN_EXTRACTOPEN pfnExtractOpen; | ||
| 121 | //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream; | ||
| 122 | //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile; | ||
| 123 | //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer; | ||
| 124 | //PFN_EXTRACTCLOSE pfnExtractClose; | ||
| 125 | //void* pCookie; | ||
| 126 | BURN_CONTAINER_TYPE type; | ||
| 127 | union | ||
| 128 | { | ||
| 129 | BURN_CONTAINER_CONTEXT_CABINET Cabinet; | ||
| 130 | }; | ||
| 131 | |||
| 132 | } BURN_CONTAINER_CONTEXT; | ||
| 133 | |||
| 134 | |||
| 135 | // functions | ||
| 136 | |||
| 137 | HRESULT ContainersParseFromXml( | ||
| 138 | __in BURN_SECTION* pSection, | ||
| 139 | __in BURN_CONTAINERS* pContainers, | ||
| 140 | __in IXMLDOMNode* pixnBundle | ||
| 141 | ); | ||
| 142 | void ContainersUninitialize( | ||
| 143 | __in BURN_CONTAINERS* pContainers | ||
| 144 | ); | ||
| 145 | HRESULT ContainerOpenUX( | ||
| 146 | __in BURN_SECTION* pSection, | ||
| 147 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 148 | ); | ||
| 149 | HRESULT ContainerOpen( | ||
| 150 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 151 | __in BURN_CONTAINER* pContainer, | ||
| 152 | __in HANDLE hContainerFile, | ||
| 153 | __in_z LPCWSTR wzFilePath | ||
| 154 | ); | ||
| 155 | HRESULT ContainerNextStream( | ||
| 156 | __inout BURN_CONTAINER_CONTEXT* pContext, | ||
| 157 | __inout_z LPWSTR* psczStreamName | ||
| 158 | ); | ||
| 159 | HRESULT ContainerStreamToFile( | ||
| 160 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 161 | __in_z LPCWSTR wzFileName | ||
| 162 | ); | ||
| 163 | HRESULT ContainerStreamToBuffer( | ||
| 164 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
| 165 | __out BYTE** ppbBuffer, | ||
| 166 | __out SIZE_T* pcbBuffer | ||
| 167 | ); | ||
| 168 | HRESULT ContainerSkipStream( | ||
| 169 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 170 | ); | ||
| 171 | HRESULT ContainerClose( | ||
| 172 | __in BURN_CONTAINER_CONTEXT* pContext | ||
| 173 | ); | ||
| 174 | HRESULT ContainerFindById( | ||
| 175 | __in BURN_CONTAINERS* pContainers, | ||
| 176 | __in_z LPCWSTR wzId, | ||
| 177 | __out BURN_CONTAINER** ppContainer | ||
| 178 | ); | ||
| 179 | |||
| 180 | |||
| 181 | #if defined(__cplusplus) | ||
| 182 | } | ||
| 183 | #endif | ||
diff --git a/src/engine/core.cpp b/src/engine/core.cpp new file mode 100644 index 00000000..519012e9 --- /dev/null +++ b/src/engine/core.cpp | |||
| @@ -0,0 +1,1705 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // structs | ||
| 7 | |||
| 8 | struct BURN_CACHE_THREAD_CONTEXT | ||
| 9 | { | ||
| 10 | BURN_ENGINE_STATE* pEngineState; | ||
| 11 | DWORD* pcOverallProgressTicks; | ||
| 12 | BOOL* pfRollback; | ||
| 13 | }; | ||
| 14 | |||
| 15 | |||
| 16 | // internal function declarations | ||
| 17 | |||
| 18 | static HRESULT ParseCommandLine( | ||
| 19 | __in int argc, | ||
| 20 | __in LPWSTR* argv, | ||
| 21 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 22 | __in BURN_PIPE_CONNECTION* pCompanionConnection, | ||
| 23 | __in BURN_PIPE_CONNECTION* pEmbeddedConnection, | ||
| 24 | __in BURN_VARIABLES* pVariables, | ||
| 25 | __out BURN_MODE* pMode, | ||
| 26 | __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, | ||
| 27 | __out BOOL* pfDisableSystemRestore, | ||
| 28 | __out_z LPWSTR* psczSourceProcessPath, | ||
| 29 | __out_z LPWSTR* psczOriginalSource, | ||
| 30 | __out BOOL* pfDisableUnelevate, | ||
| 31 | __out DWORD *pdwLoggingAttributes, | ||
| 32 | __out_z LPWSTR* psczLogFile, | ||
| 33 | __out_z LPWSTR* psczActiveParent, | ||
| 34 | __out_z LPWSTR* psczIgnoreDependencies, | ||
| 35 | __out_z LPWSTR* psczAncestors, | ||
| 36 | __out_z LPWSTR* psczSanitizedCommandLine | ||
| 37 | ); | ||
| 38 | static HRESULT ParsePipeConnection( | ||
| 39 | __in LPWSTR* rgArgs, | ||
| 40 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 41 | ); | ||
| 42 | static HRESULT DetectPackage( | ||
| 43 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 44 | __in BURN_PACKAGE* pPackage | ||
| 45 | ); | ||
| 46 | static HRESULT DetectPackagePayloadsCached( | ||
| 47 | __in BURN_PACKAGE* pPackage | ||
| 48 | ); | ||
| 49 | static DWORD WINAPI CacheThreadProc( | ||
| 50 | __in LPVOID lpThreadParameter | ||
| 51 | ); | ||
| 52 | static HRESULT WaitForCacheThread( | ||
| 53 | __in HANDLE hCacheThread | ||
| 54 | ); | ||
| 55 | static void LogPackages( | ||
| 56 | __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, | ||
| 57 | __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, | ||
| 58 | __in const BURN_PACKAGES* pPackages, | ||
| 59 | __in const BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 60 | __in const BOOTSTRAPPER_ACTION action | ||
| 61 | ); | ||
| 62 | |||
| 63 | |||
| 64 | // function definitions | ||
| 65 | |||
| 66 | extern "C" HRESULT CoreInitialize( | ||
| 67 | __in BURN_ENGINE_STATE* pEngineState | ||
| 68 | ) | ||
| 69 | { | ||
| 70 | HRESULT hr = S_OK; | ||
| 71 | LPWSTR sczSanitizedCommandLine = NULL; | ||
| 72 | LPWSTR sczStreamName = NULL; | ||
| 73 | BYTE* pbBuffer = NULL; | ||
| 74 | SIZE_T cbBuffer = 0; | ||
| 75 | BURN_CONTAINER_CONTEXT containerContext = { }; | ||
| 76 | BOOL fElevated = FALSE; | ||
| 77 | LPWSTR sczSourceProcessPath = NULL; | ||
| 78 | LPWSTR sczSourceProcessFolder = NULL; | ||
| 79 | LPWSTR sczOriginalSource = NULL; | ||
| 80 | |||
| 81 | // Initialize variables. | ||
| 82 | hr = VariableInitialize(&pEngineState->variables); | ||
| 83 | ExitOnFailure(hr, "Failed to initialize variables."); | ||
| 84 | |||
| 85 | // Open attached UX container. | ||
| 86 | hr = ContainerOpenUX(&pEngineState->section, &containerContext); | ||
| 87 | ExitOnFailure(hr, "Failed to open attached UX container."); | ||
| 88 | |||
| 89 | // Load manifest. | ||
| 90 | hr = ContainerNextStream(&containerContext, &sczStreamName); | ||
| 91 | ExitOnFailure(hr, "Failed to open manifest stream."); | ||
| 92 | |||
| 93 | hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer); | ||
| 94 | ExitOnFailure(hr, "Failed to get manifest stream from container."); | ||
| 95 | |||
| 96 | hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState); | ||
| 97 | ExitOnFailure(hr, "Failed to load manifest."); | ||
| 98 | |||
| 99 | // Parse command line. | ||
| 100 | hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine); | ||
| 101 | ExitOnFailure(hr, "Failed to parse command line."); | ||
| 102 | |||
| 103 | LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L""); | ||
| 104 | |||
| 105 | // Retain whether bundle was initially run elevated. | ||
| 106 | ProcElevated(::GetCurrentProcess(), &fElevated); | ||
| 107 | |||
| 108 | hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE); | ||
| 109 | ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); | ||
| 110 | |||
| 111 | hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE); | ||
| 112 | ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL); | ||
| 113 | |||
| 114 | if (sczSourceProcessPath) | ||
| 115 | { | ||
| 116 | hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE); | ||
| 117 | ExitOnFailure(hr, "Failed to set source process path variable."); | ||
| 118 | |||
| 119 | hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder); | ||
| 120 | ExitOnFailure(hr, "Failed to get source process folder from path."); | ||
| 121 | |||
| 122 | hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE); | ||
| 123 | ExitOnFailure(hr, "Failed to set source process folder variable."); | ||
| 124 | } | ||
| 125 | |||
| 126 | // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line. | ||
| 127 | // Needs to be done after ManifestLoadXmlFromBuffer. | ||
| 128 | if (sczOriginalSource) | ||
| 129 | { | ||
| 130 | hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE); | ||
| 131 | ExitOnFailure(hr, "Failed to set original source variable."); | ||
| 132 | } | ||
| 133 | |||
| 134 | if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) | ||
| 135 | { | ||
| 136 | hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath); | ||
| 137 | ExitOnFailure(hr, "Failed to initialize internal cache functionality."); | ||
| 138 | } | ||
| 139 | |||
| 140 | // If we're not elevated then we'll be loading the bootstrapper application, so extract | ||
| 141 | // the payloads from the BA container. | ||
| 142 | if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode) | ||
| 143 | { | ||
| 144 | // Extract all UX payloads to working folder. | ||
| 145 | hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory); | ||
| 146 | ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application."); | ||
| 147 | |||
| 148 | hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory); | ||
| 149 | ExitOnFailure(hr, "Failed to extract bootstrapper application payloads."); | ||
| 150 | |||
| 151 | // Load the catalog files as soon as they are extracted. | ||
| 152 | hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads); | ||
| 153 | ExitOnFailure(hr, "Failed to load catalog files."); | ||
| 154 | } | ||
| 155 | |||
| 156 | LExit: | ||
| 157 | ReleaseStr(sczOriginalSource); | ||
| 158 | ReleaseStr(sczSourceProcessFolder); | ||
| 159 | ReleaseStr(sczSourceProcessPath); | ||
| 160 | ContainerClose(&containerContext); | ||
| 161 | ReleaseStr(sczStreamName); | ||
| 162 | ReleaseStr(sczSanitizedCommandLine); | ||
| 163 | ReleaseMem(pbBuffer); | ||
| 164 | |||
| 165 | return hr; | ||
| 166 | } | ||
| 167 | |||
| 168 | extern "C" HRESULT CoreSerializeEngineState( | ||
| 169 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 170 | __inout BYTE** ppbBuffer, | ||
| 171 | __inout SIZE_T* piBuffer | ||
| 172 | ) | ||
| 173 | { | ||
| 174 | HRESULT hr = S_OK; | ||
| 175 | |||
| 176 | hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer); | ||
| 177 | ExitOnFailure(hr, "Failed to serialize variables."); | ||
| 178 | |||
| 179 | LExit: | ||
| 180 | return hr; | ||
| 181 | } | ||
| 182 | |||
| 183 | extern "C" HRESULT CoreQueryRegistration( | ||
| 184 | __in BURN_ENGINE_STATE* pEngineState | ||
| 185 | ) | ||
| 186 | { | ||
| 187 | HRESULT hr = S_OK; | ||
| 188 | BYTE* pbBuffer = NULL; | ||
| 189 | SIZE_T cbBuffer = 0; | ||
| 190 | SIZE_T iBuffer = 0; | ||
| 191 | |||
| 192 | // Detect if bundle is already installed. | ||
| 193 | hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); | ||
| 194 | ExitOnFailure(hr, "Failed to detect bundle install state."); | ||
| 195 | |||
| 196 | // detect resume type | ||
| 197 | hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType); | ||
| 198 | ExitOnFailure(hr, "Failed to detect resume type."); | ||
| 199 | |||
| 200 | // If we have a resume mode that suggests the bundle might already be present, try to load any | ||
| 201 | // previously stored state. | ||
| 202 | if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType) | ||
| 203 | { | ||
| 204 | // load resume state | ||
| 205 | hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer); | ||
| 206 | if (SUCCEEDED(hr)) | ||
| 207 | { | ||
| 208 | hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer); | ||
| 209 | } | ||
| 210 | |||
| 211 | // Log any failures and continue. | ||
| 212 | if (FAILED(hr)) | ||
| 213 | { | ||
| 214 | LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile); | ||
| 215 | hr = S_OK; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | LExit: | ||
| 220 | ReleaseBuffer(pbBuffer); | ||
| 221 | |||
| 222 | return hr; | ||
| 223 | } | ||
| 224 | |||
| 225 | extern "C" HRESULT CoreDetect( | ||
| 226 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 227 | __in_opt HWND hwndParent | ||
| 228 | ) | ||
| 229 | { | ||
| 230 | HRESULT hr = S_OK; | ||
| 231 | BOOL fActivated = FALSE; | ||
| 232 | BOOL fDetectBegan = FALSE; | ||
| 233 | BURN_PACKAGE* pPackage = NULL; | ||
| 234 | HRESULT hrFirstPackageFailure = S_OK; | ||
| 235 | |||
| 236 | LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages); | ||
| 237 | |||
| 238 | hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); | ||
| 239 | ExitOnFailure(hr, "Engine cannot start detect because it is busy with another action."); | ||
| 240 | |||
| 241 | // Detect if bundle installed state has changed since start up. This | ||
| 242 | // only happens if Apply() changed the state of bundle (installed or | ||
| 243 | // uninstalled). In that case, Detect() can be used here to reset | ||
| 244 | // the installed state. | ||
| 245 | hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled); | ||
| 246 | ExitOnFailure(hr, "Failed to detect bundle install state."); | ||
| 247 | |||
| 248 | if (pEngineState->registration.fInstalled) | ||
| 249 | { | ||
| 250 | hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE); | ||
| 251 | ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); | ||
| 252 | } | ||
| 253 | else | ||
| 254 | { | ||
| 255 | hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE); | ||
| 256 | ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable."); | ||
| 257 | } | ||
| 258 | |||
| 259 | fDetectBegan = TRUE; | ||
| 260 | hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fInstalled, pEngineState->packages.cPackages); | ||
| 261 | ExitOnRootFailure(hr, "UX aborted detect begin."); | ||
| 262 | |||
| 263 | pEngineState->userExperience.hwndDetect = hwndParent; | ||
| 264 | |||
| 265 | // Always reset the detect state which means the plan should be reset too. | ||
| 266 | DetectReset(&pEngineState->registration, &pEngineState->packages); | ||
| 267 | PlanReset(&pEngineState->plan, &pEngineState->packages); | ||
| 268 | |||
| 269 | hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables); | ||
| 270 | ExitOnFailure(hr, "Failed to execute searches."); | ||
| 271 | |||
| 272 | // Load all of the related bundles. | ||
| 273 | hr = RegistrationDetectRelatedBundles(&pEngineState->registration); | ||
| 274 | ExitOnFailure(hr, "Failed to detect related bundles."); | ||
| 275 | |||
| 276 | hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration); | ||
| 277 | if (SUCCEEDED(hr)) | ||
| 278 | { | ||
| 279 | hr = DetectForwardCompatibleBundle(&pEngineState->userExperience, &pEngineState->command, &pEngineState->registration); | ||
| 280 | ExitOnFailure(hr, "Failed to detect forward compatible bundle."); | ||
| 281 | |||
| 282 | // If a forward compatible bundle was detected, skip rest of bundle detection | ||
| 283 | // since we will passthrough. | ||
| 284 | if (pEngineState->registration.fEnabledForwardCompatibleBundle) | ||
| 285 | { | ||
| 286 | ExitFunction(); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | else if (E_NOTFOUND == hr) | ||
| 290 | { | ||
| 291 | hr = S_OK; | ||
| 292 | } | ||
| 293 | ExitOnFailure(hr, "Failed to detect provider key bundle id."); | ||
| 294 | |||
| 295 | // Report the related bundles. | ||
| 296 | hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action); | ||
| 297 | ExitOnFailure(hr, "Failed to report detected related bundles."); | ||
| 298 | |||
| 299 | // Do update detection. | ||
| 300 | hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update); | ||
| 301 | ExitOnFailure(hr, "Failed to detect update."); | ||
| 302 | |||
| 303 | // Detecting MSPs requires special initialization before processing each package but | ||
| 304 | // only do the detection if there are actually patch packages to detect because it | ||
| 305 | // can be expensive. | ||
| 306 | if (pEngineState->packages.cPatchInfo) | ||
| 307 | { | ||
| 308 | hr = MspEngineDetectInitialize(&pEngineState->packages); | ||
| 309 | ExitOnFailure(hr, "Failed to initialize MSP engine detection."); | ||
| 310 | } | ||
| 311 | |||
| 312 | for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) | ||
| 313 | { | ||
| 314 | pPackage = pEngineState->packages.rgPackages + i; | ||
| 315 | |||
| 316 | hr = DetectPackage(pEngineState, pPackage); | ||
| 317 | |||
| 318 | // If the package detection failed, ensure the package state is set to unknown. | ||
| 319 | if (FAILED(hr)) | ||
| 320 | { | ||
| 321 | if (SUCCEEDED(hrFirstPackageFailure)) | ||
| 322 | { | ||
| 323 | hrFirstPackageFailure = hr; | ||
| 324 | } | ||
| 325 | |||
| 326 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | // Log the detected states. | ||
| 331 | for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage) | ||
| 332 | { | ||
| 333 | pPackage = pEngineState->packages.rgPackages + iPackage; | ||
| 334 | |||
| 335 | LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache)); | ||
| 336 | |||
| 337 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type) | ||
| 338 | { | ||
| 339 | for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) | ||
| 340 | { | ||
| 341 | const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; | ||
| 342 | LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState)); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 346 | { | ||
| 347 | for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct) | ||
| 348 | { | ||
| 349 | const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct; | ||
| 350 | LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState)); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | LExit: | ||
| 356 | if (SUCCEEDED(hr)) | ||
| 357 | { | ||
| 358 | hr = hrFirstPackageFailure; | ||
| 359 | } | ||
| 360 | |||
| 361 | if (fActivated) | ||
| 362 | { | ||
| 363 | UserExperienceDeactivateEngine(&pEngineState->userExperience); | ||
| 364 | } | ||
| 365 | |||
| 366 | if (fDetectBegan) | ||
| 367 | { | ||
| 368 | UserExperienceOnDetectComplete(&pEngineState->userExperience, hr); | ||
| 369 | } | ||
| 370 | |||
| 371 | pEngineState->userExperience.hwndDetect = NULL; | ||
| 372 | |||
| 373 | LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr); | ||
| 374 | |||
| 375 | return hr; | ||
| 376 | } | ||
| 377 | |||
| 378 | extern "C" HRESULT CorePlan( | ||
| 379 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 380 | __in BOOTSTRAPPER_ACTION action | ||
| 381 | ) | ||
| 382 | { | ||
| 383 | HRESULT hr = S_OK; | ||
| 384 | BOOL fActivated = FALSE; | ||
| 385 | BOOL fPlanBegan = FALSE; | ||
| 386 | LPWSTR sczLayoutDirectory = NULL; | ||
| 387 | HANDLE hSyncpointEvent = NULL; | ||
| 388 | BURN_PACKAGE* pUpgradeBundlePackage = NULL; | ||
| 389 | BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL; | ||
| 390 | |||
| 391 | LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action)); | ||
| 392 | |||
| 393 | hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); | ||
| 394 | ExitOnFailure(hr, "Engine cannot start plan because it is busy with another action."); | ||
| 395 | |||
| 396 | fPlanBegan = TRUE; | ||
| 397 | hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages); | ||
| 398 | ExitOnRootFailure(hr, "BA aborted plan begin."); | ||
| 399 | |||
| 400 | // Always reset the plan. | ||
| 401 | PlanReset(&pEngineState->plan, &pEngineState->packages); | ||
| 402 | |||
| 403 | // Remember the overall action state in the plan since it shapes the changes | ||
| 404 | // we make everywhere. | ||
| 405 | pEngineState->plan.action = action; | ||
| 406 | pEngineState->plan.wzBundleId = pEngineState->registration.sczId; | ||
| 407 | pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId; | ||
| 408 | |||
| 409 | hr = PlanSetVariables(action, &pEngineState->variables); | ||
| 410 | ExitOnFailure(hr, "Failed to update action."); | ||
| 411 | |||
| 412 | // Set resume commandline | ||
| 413 | hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log); | ||
| 414 | ExitOnFailure(hr, "Failed to set resume command"); | ||
| 415 | |||
| 416 | hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan); | ||
| 417 | ExitOnFailure(hr, "Failed to initialize the dependencies for the plan."); | ||
| 418 | |||
| 419 | if (BOOTSTRAPPER_ACTION_LAYOUT == action) | ||
| 420 | { | ||
| 421 | Assert(!pEngineState->plan.fPerMachine); | ||
| 422 | |||
| 423 | // Plan the bundle's layout. | ||
| 424 | hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->payloads, &sczLayoutDirectory); | ||
| 425 | ExitOnFailure(hr, "Failed to plan the layout of the bundle."); | ||
| 426 | |||
| 427 | // Plan the packages' layout. | ||
| 428 | hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent); | ||
| 429 | ExitOnFailure(hr, "Failed to plan packages."); | ||
| 430 | } | ||
| 431 | else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action) | ||
| 432 | { | ||
| 433 | Assert(!pEngineState->plan.fPerMachine); | ||
| 434 | |||
| 435 | pUpgradeBundlePackage = &pEngineState->update.package; | ||
| 436 | |||
| 437 | hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); | ||
| 438 | ExitOnFailure(hr, "Failed to plan update."); | ||
| 439 | } | ||
| 440 | else if (pEngineState->registration.fEnabledForwardCompatibleBundle) | ||
| 441 | { | ||
| 442 | Assert(!pEngineState->plan.fPerMachine); | ||
| 443 | |||
| 444 | pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle; | ||
| 445 | |||
| 446 | hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent); | ||
| 447 | ExitOnFailure(hr, "Failed to plan passthrough."); | ||
| 448 | } | ||
| 449 | else // doing an action that modifies the machine state. | ||
| 450 | { | ||
| 451 | BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration. | ||
| 452 | pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle. | ||
| 453 | |||
| 454 | hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning); | ||
| 455 | ExitOnFailure(hr, "Failed to plan registration."); | ||
| 456 | |||
| 457 | if (fContinuePlanning) | ||
| 458 | { | ||
| 459 | // Remember the early index, because we want to be able to insert some related bundles | ||
| 460 | // into the plan before other executed packages. This particularly occurs for uninstallation | ||
| 461 | // of addons and patches, which should be uninstalled before the main product. | ||
| 462 | DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions; | ||
| 463 | |||
| 464 | // Plan the related bundles first to support downgrades with ref-counting. | ||
| 465 | hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); | ||
| 466 | ExitOnFailure(hr, "Failed to plan related bundles."); | ||
| 467 | |||
| 468 | hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent); | ||
| 469 | ExitOnFailure(hr, "Failed to plan packages."); | ||
| 470 | |||
| 471 | // Schedule the update of related bundles last. | ||
| 472 | hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex); | ||
| 473 | ExitOnFailure(hr, "Failed to schedule related bundles."); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | // Remove unnecessary actions. | ||
| 478 | hr = PlanFinalizeActions(&pEngineState->plan); | ||
| 479 | ExitOnFailure(hr, "Failed to remove unnecessary actions from plan."); | ||
| 480 | |||
| 481 | // Finally, display all packages and related bundles in the log. | ||
| 482 | LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action); | ||
| 483 | |||
| 484 | #ifdef DEBUG | ||
| 485 | PlanDump(&pEngineState->plan); | ||
| 486 | #endif | ||
| 487 | |||
| 488 | LExit: | ||
| 489 | if (fActivated) | ||
| 490 | { | ||
| 491 | UserExperienceDeactivateEngine(&pEngineState->userExperience); | ||
| 492 | } | ||
| 493 | |||
| 494 | if (fPlanBegan) | ||
| 495 | { | ||
| 496 | UserExperienceOnPlanComplete(&pEngineState->userExperience, hr); | ||
| 497 | } | ||
| 498 | |||
| 499 | LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr); | ||
| 500 | ReleaseStr(sczLayoutDirectory); | ||
| 501 | |||
| 502 | return hr; | ||
| 503 | } | ||
| 504 | |||
| 505 | extern "C" HRESULT CoreElevate( | ||
| 506 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 507 | __in_opt HWND hwndParent | ||
| 508 | ) | ||
| 509 | { | ||
| 510 | HRESULT hr = S_OK; | ||
| 511 | |||
| 512 | // If the elevated companion pipe isn't created yet, let's make that happen. | ||
| 513 | if (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe) | ||
| 514 | { | ||
| 515 | if (!pEngineState->sczBundleEngineWorkingPath) | ||
| 516 | { | ||
| 517 | hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); | ||
| 518 | ExitOnFailure(hr, "Failed to cache engine to working directory."); | ||
| 519 | } | ||
| 520 | |||
| 521 | hr = ElevationElevate(pEngineState, hwndParent); | ||
| 522 | ExitOnFailure(hr, "Failed to actually elevate."); | ||
| 523 | |||
| 524 | hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE); | ||
| 525 | ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED); | ||
| 526 | } | ||
| 527 | |||
| 528 | LExit: | ||
| 529 | return hr; | ||
| 530 | } | ||
| 531 | |||
| 532 | extern "C" HRESULT CoreApply( | ||
| 533 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 534 | __in_opt HWND hwndParent | ||
| 535 | ) | ||
| 536 | { | ||
| 537 | HRESULT hr = S_OK; | ||
| 538 | BOOL fActivated = FALSE; | ||
| 539 | HANDLE hLock = NULL; | ||
| 540 | DWORD cOverallProgressTicks = 0; | ||
| 541 | HANDLE hCacheThread = NULL; | ||
| 542 | BOOL fElevated = FALSE; | ||
| 543 | BOOL fRegistered = FALSE; | ||
| 544 | BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault; | ||
| 545 | BOOL fRollback = FALSE; | ||
| 546 | BOOL fSuspend = FALSE; | ||
| 547 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 548 | BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { }; | ||
| 549 | DWORD dwPhaseCount = 0; | ||
| 550 | BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; | ||
| 551 | |||
| 552 | LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); | ||
| 553 | |||
| 554 | hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); | ||
| 555 | ExitOnFailure(hr, "Engine cannot start apply because it is busy with another action."); | ||
| 556 | |||
| 557 | // Ensure any previous attempts to execute are reset. | ||
| 558 | ApplyReset(&pEngineState->userExperience, &pEngineState->packages); | ||
| 559 | |||
| 560 | if (pEngineState->plan.cCacheActions) | ||
| 561 | { | ||
| 562 | ++dwPhaseCount; | ||
| 563 | } | ||
| 564 | if (pEngineState->plan.cExecuteActions) | ||
| 565 | { | ||
| 566 | ++dwPhaseCount; | ||
| 567 | } | ||
| 568 | |||
| 569 | hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); | ||
| 570 | ExitOnRootFailure(hr, "BA aborted apply begin."); | ||
| 571 | |||
| 572 | // Abort if this bundle already requires a restart. | ||
| 573 | if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType) | ||
| 574 | { | ||
| 575 | restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 576 | |||
| 577 | hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT); | ||
| 578 | UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value. | ||
| 579 | ExitFunction(); | ||
| 580 | } | ||
| 581 | |||
| 582 | hr = ApplyLock(FALSE, &hLock); | ||
| 583 | ExitOnFailure(hr, "Another per-user setup is already executing."); | ||
| 584 | |||
| 585 | // Initialize only after getting a lock. | ||
| 586 | ApplyInitialize(); | ||
| 587 | |||
| 588 | pEngineState->userExperience.hwndApply = hwndParent; | ||
| 589 | |||
| 590 | hr = ApplySetVariables(&pEngineState->variables); | ||
| 591 | ExitOnFailure(hr, "Failed to set initial apply variables."); | ||
| 592 | |||
| 593 | // If the plan is empty of work to do, skip everything. | ||
| 594 | if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions)) | ||
| 595 | { | ||
| 596 | LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED); | ||
| 597 | ExitFunction(); | ||
| 598 | } | ||
| 599 | |||
| 600 | // Ensure the engine is cached to the working path. | ||
| 601 | if (!pEngineState->sczBundleEngineWorkingPath) | ||
| 602 | { | ||
| 603 | hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath); | ||
| 604 | ExitOnFailure(hr, "Failed to cache engine to working directory."); | ||
| 605 | } | ||
| 606 | |||
| 607 | // Elevate. | ||
| 608 | if (pEngineState->plan.fPerMachine) | ||
| 609 | { | ||
| 610 | hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); | ||
| 611 | ExitOnFailure(hr, "Failed to elevate."); | ||
| 612 | |||
| 613 | hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore); | ||
| 614 | ExitOnFailure(hr, "Another per-machine setup is already executing."); | ||
| 615 | |||
| 616 | fElevated = TRUE; | ||
| 617 | } | ||
| 618 | |||
| 619 | // Register. | ||
| 620 | if (pEngineState->plan.fRegister) | ||
| 621 | { | ||
| 622 | hr = ApplyRegister(pEngineState); | ||
| 623 | ExitOnFailure(hr, "Failed to register bundle."); | ||
| 624 | fRegistered = TRUE; | ||
| 625 | } | ||
| 626 | |||
| 627 | // Cache. | ||
| 628 | if (pEngineState->plan.cCacheActions) | ||
| 629 | { | ||
| 630 | // Launch the cache thread. | ||
| 631 | cacheThreadContext.pEngineState = pEngineState; | ||
| 632 | cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks; | ||
| 633 | cacheThreadContext.pfRollback = &fRollback; | ||
| 634 | |||
| 635 | hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL); | ||
| 636 | ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread."); | ||
| 637 | |||
| 638 | // If we're not caching in parallel, wait for the cache thread to terminate. | ||
| 639 | if (!pEngineState->fParallelCacheAndExecute) | ||
| 640 | { | ||
| 641 | hr = WaitForCacheThread(hCacheThread); | ||
| 642 | ExitOnFailure(hr, "Failed while caching, aborting execution."); | ||
| 643 | |||
| 644 | ReleaseHandle(hCacheThread); | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | // Execute. | ||
| 649 | if (pEngineState->plan.cExecuteActions) | ||
| 650 | { | ||
| 651 | hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fKeepRegistration, &fRollback, &fSuspend, &restart); | ||
| 652 | UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed. | ||
| 653 | } | ||
| 654 | |||
| 655 | // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete. | ||
| 656 | if (hCacheThread) | ||
| 657 | { | ||
| 658 | HRESULT hrCached = WaitForCacheThread(hCacheThread); | ||
| 659 | if (SUCCEEDED(hr)) | ||
| 660 | { | ||
| 661 | hr = hrCached; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | // If something went wrong or force restarted, skip cleaning. | ||
| 666 | if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
| 667 | { | ||
| 668 | ExitFunction(); | ||
| 669 | } | ||
| 670 | |||
| 671 | // Clean. | ||
| 672 | if (pEngineState->plan.cCleanActions) | ||
| 673 | { | ||
| 674 | ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe); | ||
| 675 | } | ||
| 676 | |||
| 677 | LExit: | ||
| 678 | // Unregister. | ||
| 679 | if (fRegistered) | ||
| 680 | { | ||
| 681 | ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fKeepRegistration || pEngineState->plan.fDisallowRemoval, fSuspend, restart); | ||
| 682 | } | ||
| 683 | |||
| 684 | if (fElevated) | ||
| 685 | { | ||
| 686 | ElevationApplyUninitialize(pEngineState->companionConnection.hPipe); | ||
| 687 | } | ||
| 688 | |||
| 689 | pEngineState->userExperience.hwndApply = NULL; | ||
| 690 | |||
| 691 | ApplyUninitialize(); | ||
| 692 | |||
| 693 | if (hLock) | ||
| 694 | { | ||
| 695 | ::ReleaseMutex(hLock); | ||
| 696 | ::CloseHandle(hLock); | ||
| 697 | } | ||
| 698 | |||
| 699 | if (fActivated) | ||
| 700 | { | ||
| 701 | UserExperienceDeactivateEngine(&pEngineState->userExperience); | ||
| 702 | } | ||
| 703 | |||
| 704 | ReleaseHandle(hCacheThread); | ||
| 705 | |||
| 706 | UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); | ||
| 707 | if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) | ||
| 708 | { | ||
| 709 | pEngineState->fRestart = TRUE; | ||
| 710 | } | ||
| 711 | |||
| 712 | LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); | ||
| 713 | |||
| 714 | return hr; | ||
| 715 | } | ||
| 716 | |||
| 717 | extern "C" HRESULT CoreLaunchApprovedExe( | ||
| 718 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 719 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 720 | ) | ||
| 721 | { | ||
| 722 | HRESULT hr = S_OK; | ||
| 723 | BOOL fActivated = FALSE; | ||
| 724 | DWORD dwProcessId = 0; | ||
| 725 | |||
| 726 | LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId); | ||
| 727 | |||
| 728 | hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated); | ||
| 729 | ExitOnFailure(hr, "Engine cannot start LaunchApprovedExe because it is busy with another action."); | ||
| 730 | |||
| 731 | hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience); | ||
| 732 | ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); | ||
| 733 | |||
| 734 | // Elevate. | ||
| 735 | hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent); | ||
| 736 | ExitOnFailure(hr, "Failed to elevate."); | ||
| 737 | |||
| 738 | // Launch. | ||
| 739 | hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId); | ||
| 740 | |||
| 741 | LExit: | ||
| 742 | if (fActivated) | ||
| 743 | { | ||
| 744 | UserExperienceDeactivateEngine(&pEngineState->userExperience); | ||
| 745 | } | ||
| 746 | |||
| 747 | UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId); | ||
| 748 | |||
| 749 | LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId); | ||
| 750 | |||
| 751 | ApprovedExesUninitializeLaunch(pLaunchApprovedExe); | ||
| 752 | |||
| 753 | return hr; | ||
| 754 | } | ||
| 755 | |||
| 756 | extern "C" HRESULT CoreQuit( | ||
| 757 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 758 | __in int nExitCode | ||
| 759 | ) | ||
| 760 | { | ||
| 761 | HRESULT hr = S_OK; | ||
| 762 | |||
| 763 | // Save engine state if resume mode is unequal to "none". | ||
| 764 | if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode) | ||
| 765 | { | ||
| 766 | hr = CoreSaveEngineState(pEngineState); | ||
| 767 | if (FAILED(hr)) | ||
| 768 | { | ||
| 769 | LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL); | ||
| 770 | hr = S_OK; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | LogId(REPORT_STANDARD, MSG_QUIT, nExitCode); | ||
| 775 | |||
| 776 | ::PostQuitMessage(nExitCode); // go bye-bye. | ||
| 777 | |||
| 778 | return hr; | ||
| 779 | } | ||
| 780 | |||
| 781 | extern "C" HRESULT CoreSaveEngineState( | ||
| 782 | __in BURN_ENGINE_STATE* pEngineState | ||
| 783 | ) | ||
| 784 | { | ||
| 785 | HRESULT hr = S_OK; | ||
| 786 | BYTE* pbBuffer = NULL; | ||
| 787 | SIZE_T cbBuffer = 0; | ||
| 788 | |||
| 789 | // serialize engine state | ||
| 790 | hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer); | ||
| 791 | ExitOnFailure(hr, "Failed to serialize engine state."); | ||
| 792 | |||
| 793 | // write to registration store | ||
| 794 | if (pEngineState->registration.fPerMachine) | ||
| 795 | { | ||
| 796 | hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer); | ||
| 797 | ExitOnFailure(hr, "Failed to save engine state in per-machine process."); | ||
| 798 | } | ||
| 799 | else | ||
| 800 | { | ||
| 801 | hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer); | ||
| 802 | ExitOnFailure(hr, "Failed to save engine state."); | ||
| 803 | } | ||
| 804 | |||
| 805 | LExit: | ||
| 806 | ReleaseBuffer(pbBuffer); | ||
| 807 | |||
| 808 | return hr; | ||
| 809 | } | ||
| 810 | |||
| 811 | extern "C" LPCWSTR CoreRelationTypeToCommandLineString( | ||
| 812 | __in BOOTSTRAPPER_RELATION_TYPE relationType | ||
| 813 | ) | ||
| 814 | { | ||
| 815 | LPCWSTR wzRelationTypeCommandLine = NULL; | ||
| 816 | switch (relationType) | ||
| 817 | { | ||
| 818 | case BOOTSTRAPPER_RELATION_DETECT: | ||
| 819 | wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT; | ||
| 820 | break; | ||
| 821 | case BOOTSTRAPPER_RELATION_UPGRADE: | ||
| 822 | wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE; | ||
| 823 | break; | ||
| 824 | case BOOTSTRAPPER_RELATION_ADDON: | ||
| 825 | wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON; | ||
| 826 | break; | ||
| 827 | case BOOTSTRAPPER_RELATION_PATCH: | ||
| 828 | wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH; | ||
| 829 | break; | ||
| 830 | case BOOTSTRAPPER_RELATION_UPDATE: | ||
| 831 | wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE; | ||
| 832 | break; | ||
| 833 | case BOOTSTRAPPER_RELATION_DEPENDENT: | ||
| 834 | break; | ||
| 835 | case BOOTSTRAPPER_RELATION_NONE: __fallthrough; | ||
| 836 | default: | ||
| 837 | wzRelationTypeCommandLine = NULL; | ||
| 838 | break; | ||
| 839 | } | ||
| 840 | |||
| 841 | return wzRelationTypeCommandLine; | ||
| 842 | } | ||
| 843 | |||
| 844 | extern "C" HRESULT CoreRecreateCommandLine( | ||
| 845 | __deref_inout_z LPWSTR* psczCommandLine, | ||
| 846 | __in BOOTSTRAPPER_ACTION action, | ||
| 847 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 848 | __in BOOTSTRAPPER_RESTART restart, | ||
| 849 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 850 | __in BOOL fPassthrough, | ||
| 851 | __in_z_opt LPCWSTR wzActiveParent, | ||
| 852 | __in_z_opt LPCWSTR wzAncestors, | ||
| 853 | __in_z_opt LPCWSTR wzAppendLogPath, | ||
| 854 | __in_z_opt LPCWSTR wzAdditionalCommandLineArguments | ||
| 855 | ) | ||
| 856 | { | ||
| 857 | HRESULT hr = S_OK; | ||
| 858 | LPWSTR scz = NULL; | ||
| 859 | LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); | ||
| 860 | |||
| 861 | hr = StrAllocString(psczCommandLine, L"", 0); | ||
| 862 | ExitOnFailure(hr, "Failed to empty command line."); | ||
| 863 | |||
| 864 | switch (display) | ||
| 865 | { | ||
| 866 | case BOOTSTRAPPER_DISPLAY_NONE: | ||
| 867 | hr = StrAllocConcat(psczCommandLine, L" /quiet", 0); | ||
| 868 | break; | ||
| 869 | case BOOTSTRAPPER_DISPLAY_PASSIVE: | ||
| 870 | hr = StrAllocConcat(psczCommandLine, L" /passive", 0); | ||
| 871 | break; | ||
| 872 | } | ||
| 873 | ExitOnFailure(hr, "Failed to append display state to command-line"); | ||
| 874 | |||
| 875 | switch (action) | ||
| 876 | { | ||
| 877 | case BOOTSTRAPPER_ACTION_MODIFY: | ||
| 878 | hr = StrAllocConcat(psczCommandLine, L" /modify", 0); | ||
| 879 | break; | ||
| 880 | case BOOTSTRAPPER_ACTION_REPAIR: | ||
| 881 | hr = StrAllocConcat(psczCommandLine, L" /repair", 0); | ||
| 882 | break; | ||
| 883 | case BOOTSTRAPPER_ACTION_UNINSTALL: | ||
| 884 | hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0); | ||
| 885 | break; | ||
| 886 | } | ||
| 887 | ExitOnFailure(hr, "Failed to append action state to command-line"); | ||
| 888 | |||
| 889 | switch (restart) | ||
| 890 | { | ||
| 891 | case BOOTSTRAPPER_RESTART_ALWAYS: | ||
| 892 | hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0); | ||
| 893 | break; | ||
| 894 | case BOOTSTRAPPER_RESTART_NEVER: | ||
| 895 | hr = StrAllocConcat(psczCommandLine, L" /norestart", 0); | ||
| 896 | break; | ||
| 897 | } | ||
| 898 | ExitOnFailure(hr, "Failed to append restart state to command-line"); | ||
| 899 | |||
| 900 | if (wzActiveParent) | ||
| 901 | { | ||
| 902 | if (*wzActiveParent) | ||
| 903 | { | ||
| 904 | hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent); | ||
| 905 | ExitOnFailure(hr, "Failed to format active parent command-line for command-line."); | ||
| 906 | } | ||
| 907 | else | ||
| 908 | { | ||
| 909 | hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE); | ||
| 910 | ExitOnFailure(hr, "Failed to format parent:none command-line for command-line."); | ||
| 911 | } | ||
| 912 | |||
| 913 | hr = StrAllocConcat(psczCommandLine, scz, 0); | ||
| 914 | ExitOnFailure(hr, "Failed to append active parent command-line to command-line."); | ||
| 915 | } | ||
| 916 | |||
| 917 | if (wzAncestors) | ||
| 918 | { | ||
| 919 | hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors); | ||
| 920 | ExitOnFailure(hr, "Failed to format ancestors for command-line."); | ||
| 921 | |||
| 922 | hr = StrAllocConcat(psczCommandLine, scz, 0); | ||
| 923 | ExitOnFailure(hr, "Failed to append ancestors to command-line."); | ||
| 924 | } | ||
| 925 | |||
| 926 | if (wzRelationTypeCommandLine) | ||
| 927 | { | ||
| 928 | hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine); | ||
| 929 | ExitOnFailure(hr, "Failed to format relation type for command-line."); | ||
| 930 | |||
| 931 | hr = StrAllocConcat(psczCommandLine, scz, 0); | ||
| 932 | ExitOnFailure(hr, "Failed to append relation type to command-line."); | ||
| 933 | } | ||
| 934 | |||
| 935 | if (fPassthrough) | ||
| 936 | { | ||
| 937 | hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH); | ||
| 938 | ExitOnFailure(hr, "Failed to format passthrough for command-line."); | ||
| 939 | |||
| 940 | hr = StrAllocConcat(psczCommandLine, scz, 0); | ||
| 941 | ExitOnFailure(hr, "Failed to append passthrough to command-line."); | ||
| 942 | } | ||
| 943 | |||
| 944 | if (wzAppendLogPath && *wzAppendLogPath) | ||
| 945 | { | ||
| 946 | hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath); | ||
| 947 | ExitOnFailure(hr, "Failed to format append log command-line for command-line."); | ||
| 948 | |||
| 949 | hr = StrAllocConcat(psczCommandLine, scz, 0); | ||
| 950 | ExitOnFailure(hr, "Failed to append log command-line to command-line"); | ||
| 951 | } | ||
| 952 | |||
| 953 | if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments) | ||
| 954 | { | ||
| 955 | hr = StrAllocConcat(psczCommandLine, L" ", 0); | ||
| 956 | ExitOnFailure(hr, "Failed to append space to command-line."); | ||
| 957 | |||
| 958 | hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0); | ||
| 959 | ExitOnFailure(hr, "Failed to append command-line to command-line."); | ||
| 960 | } | ||
| 961 | |||
| 962 | LExit: | ||
| 963 | ReleaseStr(scz); | ||
| 964 | |||
| 965 | return hr; | ||
| 966 | } | ||
| 967 | |||
| 968 | extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine( | ||
| 969 | __in HANDLE hFileWithAttachedContainer, | ||
| 970 | __out HANDLE* phExecutableFile, | ||
| 971 | __deref_inout_z LPWSTR* psczCommandLine | ||
| 972 | ) | ||
| 973 | { | ||
| 974 | HRESULT hr = S_OK; | ||
| 975 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; | ||
| 976 | |||
| 977 | *phExecutableFile = INVALID_HANDLE_VALUE; | ||
| 978 | |||
| 979 | if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) | ||
| 980 | { | ||
| 981 | ExitWithLastError(hr, "Failed to duplicate file handle for attached container."); | ||
| 982 | } | ||
| 983 | |||
| 984 | hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, hExecutableFile); | ||
| 985 | ExitOnFailure(hr, "Failed to append the file handle to the command line."); | ||
| 986 | |||
| 987 | *phExecutableFile = hExecutableFile; | ||
| 988 | hExecutableFile = INVALID_HANDLE_VALUE; | ||
| 989 | |||
| 990 | LExit: | ||
| 991 | ReleaseFileHandle(hExecutableFile); | ||
| 992 | |||
| 993 | return hr; | ||
| 994 | } | ||
| 995 | |||
| 996 | extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine( | ||
| 997 | __in LPCWSTR wzExecutablePath, | ||
| 998 | __out HANDLE* phExecutableFile, | ||
| 999 | __deref_inout_z LPWSTR* psczCommandLine, | ||
| 1000 | __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine | ||
| 1001 | ) | ||
| 1002 | { | ||
| 1003 | HRESULT hr = S_OK; | ||
| 1004 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; | ||
| 1005 | SECURITY_ATTRIBUTES securityAttributes = { }; | ||
| 1006 | securityAttributes.bInheritHandle = TRUE; | ||
| 1007 | *phExecutableFile = INVALID_HANDLE_VALUE; | ||
| 1008 | |||
| 1009 | hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
| 1010 | if (INVALID_HANDLE_VALUE != hExecutableFile) | ||
| 1011 | { | ||
| 1012 | hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); | ||
| 1013 | ExitOnFailure(hr, "Failed to append the file handle to the command line."); | ||
| 1014 | |||
| 1015 | if (psczObfuscatedCommandLine) | ||
| 1016 | { | ||
| 1017 | hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%u", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile); | ||
| 1018 | ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line."); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | *phExecutableFile = hExecutableFile; | ||
| 1022 | hExecutableFile = INVALID_HANDLE_VALUE; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | LExit: | ||
| 1026 | ReleaseFileHandle(hExecutableFile); | ||
| 1027 | |||
| 1028 | return hr; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | // internal helper functions | ||
| 1032 | |||
| 1033 | static HRESULT ParseCommandLine( | ||
| 1034 | __in int argc, | ||
| 1035 | __in LPWSTR* argv, | ||
| 1036 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 1037 | __in BURN_PIPE_CONNECTION* pCompanionConnection, | ||
| 1038 | __in BURN_PIPE_CONNECTION* pEmbeddedConnection, | ||
| 1039 | __in BURN_VARIABLES* pVariables, | ||
| 1040 | __out BURN_MODE* pMode, | ||
| 1041 | __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates, | ||
| 1042 | __out BOOL* pfDisableSystemRestore, | ||
| 1043 | __out_z LPWSTR* psczSourceProcessPath, | ||
| 1044 | __out_z LPWSTR* psczOriginalSource, | ||
| 1045 | __out BOOL* pfDisableUnelevate, | ||
| 1046 | __out DWORD *pdwLoggingAttributes, | ||
| 1047 | __out_z LPWSTR* psczLogFile, | ||
| 1048 | __out_z LPWSTR* psczActiveParent, | ||
| 1049 | __out_z LPWSTR* psczIgnoreDependencies, | ||
| 1050 | __out_z LPWSTR* psczAncestors, | ||
| 1051 | __out_z LPWSTR* psczSanitizedCommandLine | ||
| 1052 | ) | ||
| 1053 | { | ||
| 1054 | HRESULT hr = S_OK; | ||
| 1055 | BOOL fUnknownArg = FALSE; | ||
| 1056 | BOOL fHidden = FALSE; | ||
| 1057 | LPWSTR sczCommandLine = NULL; | ||
| 1058 | LPWSTR sczSanitizedArgument = NULL; | ||
| 1059 | LPWSTR sczVariableName = NULL; | ||
| 1060 | |||
| 1061 | for (int i = 0; i < argc; ++i) | ||
| 1062 | { | ||
| 1063 | fUnknownArg = FALSE; | ||
| 1064 | int originalIndex = i; | ||
| 1065 | ReleaseNullStr(sczSanitizedArgument); | ||
| 1066 | |||
| 1067 | if (argv[i][0] == L'-' || argv[i][0] == L'/') | ||
| 1068 | { | ||
| 1069 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) || | ||
| 1070 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1)) | ||
| 1071 | { | ||
| 1072 | *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND; | ||
| 1073 | |||
| 1074 | if (i + 1 >= argc) | ||
| 1075 | { | ||
| 1076 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log."); | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | ++i; | ||
| 1080 | |||
| 1081 | hr = StrAllocString(psczLogFile, argv[i], 0); | ||
| 1082 | ExitOnFailure(hr, "Failed to copy log file path."); | ||
| 1083 | } | ||
| 1084 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) || | ||
| 1085 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) || | ||
| 1086 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1)) | ||
| 1087 | { | ||
| 1088 | pCommand->action = BOOTSTRAPPER_ACTION_HELP; | ||
| 1089 | } | ||
| 1090 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) || | ||
| 1091 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) || | ||
| 1092 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) || | ||
| 1093 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1)) | ||
| 1094 | { | ||
| 1095 | pCommand->display = BOOTSTRAPPER_DISPLAY_NONE; | ||
| 1096 | |||
| 1097 | if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) | ||
| 1098 | { | ||
| 1099 | pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1)) | ||
| 1103 | { | ||
| 1104 | pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE; | ||
| 1105 | |||
| 1106 | if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) | ||
| 1107 | { | ||
| 1108 | pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC; | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1)) | ||
| 1112 | { | ||
| 1113 | pCommand->restart = BOOTSTRAPPER_RESTART_NEVER; | ||
| 1114 | } | ||
| 1115 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1)) | ||
| 1116 | { | ||
| 1117 | pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS; | ||
| 1118 | } | ||
| 1119 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1)) | ||
| 1120 | { | ||
| 1121 | pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; | ||
| 1122 | } | ||
| 1123 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1)) | ||
| 1124 | { | ||
| 1125 | if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) | ||
| 1126 | { | ||
| 1127 | pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | // If there is another command line argument and it is not a switch, use that as the layout directory. | ||
| 1131 | if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/') | ||
| 1132 | { | ||
| 1133 | ++i; | ||
| 1134 | |||
| 1135 | hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); | ||
| 1136 | ExitOnFailure(hr, "Failed to copy path for layout directory."); | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1)) | ||
| 1140 | { | ||
| 1141 | if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) | ||
| 1142 | { | ||
| 1143 | pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL; | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1)) | ||
| 1147 | { | ||
| 1148 | if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) | ||
| 1149 | { | ||
| 1150 | pCommand->action = BOOTSTRAPPER_ACTION_REPAIR; | ||
| 1151 | } | ||
| 1152 | } | ||
| 1153 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1)) | ||
| 1154 | { | ||
| 1155 | if (BOOTSTRAPPER_ACTION_HELP != pCommand->action) | ||
| 1156 | { | ||
| 1157 | pCommand->action = BOOTSTRAPPER_ACTION_MODIFY; | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) || | ||
| 1161 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1)) | ||
| 1162 | { | ||
| 1163 | if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) | ||
| 1164 | { | ||
| 1165 | pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; | ||
| 1166 | } | ||
| 1167 | } | ||
| 1168 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1)) | ||
| 1169 | { | ||
| 1170 | *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE; | ||
| 1171 | } | ||
| 1172 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1)) | ||
| 1173 | { | ||
| 1174 | // Switch /noaupause takes precedence. | ||
| 1175 | if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates) | ||
| 1176 | { | ||
| 1177 | *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME; | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1)) | ||
| 1181 | { | ||
| 1182 | *pfDisableSystemRestore = TRUE; | ||
| 1183 | } | ||
| 1184 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1)) | ||
| 1185 | { | ||
| 1186 | if (i + 1 >= argc) | ||
| 1187 | { | ||
| 1188 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source."); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | ++i; | ||
| 1192 | hr = StrAllocString(psczOriginalSource, argv[i], 0); | ||
| 1193 | ExitOnFailure(hr, "Failed to copy last used source."); | ||
| 1194 | } | ||
| 1195 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1)) | ||
| 1196 | { | ||
| 1197 | if (i + 1 >= argc) | ||
| 1198 | { | ||
| 1199 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent."); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | ++i; | ||
| 1203 | |||
| 1204 | hr = StrAllocString(psczActiveParent, argv[i], 0); | ||
| 1205 | ExitOnFailure(hr, "Failed to copy parent."); | ||
| 1206 | } | ||
| 1207 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1)) | ||
| 1208 | { | ||
| 1209 | hr = StrAllocString(psczActiveParent, L"", 0); | ||
| 1210 | ExitOnFailure(hr, "Failed to initialize parent to none."); | ||
| 1211 | } | ||
| 1212 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1)) | ||
| 1213 | { | ||
| 1214 | if (i + 1 >= argc) | ||
| 1215 | { | ||
| 1216 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log."); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | ++i; | ||
| 1220 | |||
| 1221 | hr = StrAllocString(psczLogFile, argv[i], 0); | ||
| 1222 | ExitOnFailure(hr, "Failed to copy append log file path."); | ||
| 1223 | |||
| 1224 | *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND; | ||
| 1225 | } | ||
| 1226 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1)) | ||
| 1227 | { | ||
| 1228 | if (i + 3 >= argc) | ||
| 1229 | { | ||
| 1230 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id."); | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | if (BURN_MODE_UNTRUSTED != *pMode) | ||
| 1234 | { | ||
| 1235 | ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | *pMode = BURN_MODE_ELEVATED; | ||
| 1239 | |||
| 1240 | ++i; | ||
| 1241 | |||
| 1242 | hr = ParsePipeConnection(argv + i, pCompanionConnection); | ||
| 1243 | ExitOnFailure(hr, "Failed to parse elevated connection."); | ||
| 1244 | |||
| 1245 | i += 2; | ||
| 1246 | } | ||
| 1247 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM))) | ||
| 1248 | { | ||
| 1249 | // Get a pointer to the next character after the switch. | ||
| 1250 | LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)]; | ||
| 1251 | if (L'=' != wzParam[0] || L'\0' == wzParam[1]) | ||
| 1252 | { | ||
| 1253 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | if (BURN_MODE_UNTRUSTED != *pMode) | ||
| 1257 | { | ||
| 1258 | ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | *pMode = BURN_MODE_NORMAL; | ||
| 1262 | |||
| 1263 | hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0); | ||
| 1264 | ExitOnFailure(hr, "Failed to copy source process path."); | ||
| 1265 | } | ||
| 1266 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1)) | ||
| 1267 | { | ||
| 1268 | if (i + 3 >= argc) | ||
| 1269 | { | ||
| 1270 | ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id."); | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | switch (*pMode) | ||
| 1274 | { | ||
| 1275 | case BURN_MODE_UNTRUSTED: | ||
| 1276 | // Leave mode as UNTRUSTED to launch the clean room process. | ||
| 1277 | break; | ||
| 1278 | case BURN_MODE_NORMAL: | ||
| 1279 | // The initialization code already assumes that the | ||
| 1280 | // clean room switch is at the beginning of the command line, | ||
| 1281 | // so it's safe to assume that the mode is NORMAL in the clean room. | ||
| 1282 | *pMode = BURN_MODE_EMBEDDED; | ||
| 1283 | break; | ||
| 1284 | default: | ||
| 1285 | ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | ++i; | ||
| 1289 | |||
| 1290 | hr = ParsePipeConnection(argv + i, pEmbeddedConnection); | ||
| 1291 | ExitOnFailure(hr, "Failed to parse embedded connection."); | ||
| 1292 | |||
| 1293 | i += 2; | ||
| 1294 | } | ||
| 1295 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1)) | ||
| 1296 | { | ||
| 1297 | pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT; | ||
| 1298 | |||
| 1299 | LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); | ||
| 1300 | } | ||
| 1301 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1)) | ||
| 1302 | { | ||
| 1303 | pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE; | ||
| 1304 | |||
| 1305 | LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); | ||
| 1306 | } | ||
| 1307 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1)) | ||
| 1308 | { | ||
| 1309 | pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON; | ||
| 1310 | |||
| 1311 | LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); | ||
| 1312 | } | ||
| 1313 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1)) | ||
| 1314 | { | ||
| 1315 | pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH; | ||
| 1316 | |||
| 1317 | LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); | ||
| 1318 | } | ||
| 1319 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1)) | ||
| 1320 | { | ||
| 1321 | pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE; | ||
| 1322 | |||
| 1323 | LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType)); | ||
| 1324 | } | ||
| 1325 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1)) | ||
| 1326 | { | ||
| 1327 | pCommand->fPassthrough = TRUE; | ||
| 1328 | } | ||
| 1329 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1)) | ||
| 1330 | { | ||
| 1331 | *pfDisableUnelevate = TRUE; | ||
| 1332 | } | ||
| 1333 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1)) | ||
| 1334 | { | ||
| 1335 | if (BURN_MODE_UNTRUSTED != *pMode) | ||
| 1336 | { | ||
| 1337 | ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided."); | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | *pMode = BURN_MODE_RUNONCE; | ||
| 1341 | } | ||
| 1342 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES))) | ||
| 1343 | { | ||
| 1344 | // Get a pointer to the next character after the switch. | ||
| 1345 | LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)]; | ||
| 1346 | if (L'=' != wzParam[0] || L'\0' == wzParam[1]) | ||
| 1347 | { | ||
| 1348 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES); | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0); | ||
| 1352 | ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); | ||
| 1353 | } | ||
| 1354 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS))) | ||
| 1355 | { | ||
| 1356 | // Get a pointer to the next character after the switch. | ||
| 1357 | LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)]; | ||
| 1358 | if (L'=' != wzParam[0] || L'\0' == wzParam[1]) | ||
| 1359 | { | ||
| 1360 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS); | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | hr = StrAllocString(psczAncestors, &wzParam[1], 0); | ||
| 1364 | ExitOnFailure(hr, "Failed to allocate the list of ancestors."); | ||
| 1365 | } | ||
| 1366 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) | ||
| 1367 | { | ||
| 1368 | // Already processed in InitializeEngineState. | ||
| 1369 | } | ||
| 1370 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) | ||
| 1371 | { | ||
| 1372 | // Already processed in InitializeEngineState. | ||
| 1373 | } | ||
| 1374 | else if (lstrlenW(&argv[i][1]) >= lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX) && | ||
| 1375 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX))) | ||
| 1376 | { | ||
| 1377 | // Skip (but log) any other private burn switches we don't recognize, so that | ||
| 1378 | // adding future private variables doesn't break old bundles | ||
| 1379 | LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]); | ||
| 1380 | } | ||
| 1381 | else | ||
| 1382 | { | ||
| 1383 | fUnknownArg = TRUE; | ||
| 1384 | } | ||
| 1385 | } | ||
| 1386 | else | ||
| 1387 | { | ||
| 1388 | fUnknownArg = TRUE; | ||
| 1389 | |||
| 1390 | const wchar_t* pwc = wcschr(argv[i], L'='); | ||
| 1391 | if (pwc) | ||
| 1392 | { | ||
| 1393 | hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); | ||
| 1394 | ExitOnFailure(hr, "Failed to copy variable name."); | ||
| 1395 | |||
| 1396 | hr = VariableIsHidden(pVariables, sczVariableName, &fHidden); | ||
| 1397 | ExitOnFailure(hr, "Failed to determine whether variable is hidden."); | ||
| 1398 | |||
| 1399 | if (fHidden) | ||
| 1400 | { | ||
| 1401 | hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName); | ||
| 1402 | ExitOnFailure(hr, "Failed to copy sanitized argument."); | ||
| 1403 | } | ||
| 1404 | } | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | // Remember command-line switch to pass off to UX. | ||
| 1408 | if (fUnknownArg) | ||
| 1409 | { | ||
| 1410 | PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]); | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | if (sczSanitizedArgument) | ||
| 1414 | { | ||
| 1415 | PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument); | ||
| 1416 | } | ||
| 1417 | else | ||
| 1418 | { | ||
| 1419 | for (; originalIndex <= i; ++originalIndex) | ||
| 1420 | { | ||
| 1421 | PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]); | ||
| 1422 | } | ||
| 1423 | } | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | // If embedded, ensure the display goes embedded as well. | ||
| 1427 | if (BURN_MODE_EMBEDDED == *pMode) | ||
| 1428 | { | ||
| 1429 | pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | // Set the defaults if nothing was set above. | ||
| 1433 | if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action) | ||
| 1434 | { | ||
| 1435 | pCommand->action = BOOTSTRAPPER_ACTION_INSTALL; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display) | ||
| 1439 | { | ||
| 1440 | pCommand->display = BOOTSTRAPPER_DISPLAY_FULL; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart) | ||
| 1444 | { | ||
| 1445 | pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT; | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | LExit: | ||
| 1449 | ReleaseStr(sczVariableName); | ||
| 1450 | ReleaseStr(sczSanitizedArgument); | ||
| 1451 | ReleaseStr(sczCommandLine); | ||
| 1452 | |||
| 1453 | return hr; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static HRESULT ParsePipeConnection( | ||
| 1457 | __in_ecount(3) LPWSTR* rgArgs, | ||
| 1458 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 1459 | ) | ||
| 1460 | { | ||
| 1461 | HRESULT hr = S_OK; | ||
| 1462 | |||
| 1463 | hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0); | ||
| 1464 | ExitOnFailure(hr, "Failed to copy connection name from command line."); | ||
| 1465 | |||
| 1466 | hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0); | ||
| 1467 | ExitOnFailure(hr, "Failed to copy connection secret from command line."); | ||
| 1468 | |||
| 1469 | hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId)); | ||
| 1470 | ExitOnFailure(hr, "Failed to copy parent process id from command line."); | ||
| 1471 | |||
| 1472 | LExit: | ||
| 1473 | return hr; | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | static HRESULT DetectPackage( | ||
| 1477 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 1478 | __in BURN_PACKAGE* pPackage | ||
| 1479 | ) | ||
| 1480 | { | ||
| 1481 | HRESULT hr = S_OK; | ||
| 1482 | BOOL fBegan = FALSE; | ||
| 1483 | |||
| 1484 | fBegan = TRUE; | ||
| 1485 | hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId); | ||
| 1486 | ExitOnRootFailure(hr, "BA aborted detect package begin."); | ||
| 1487 | |||
| 1488 | // Detect the cache state of the package. | ||
| 1489 | hr = DetectPackagePayloadsCached(pPackage); | ||
| 1490 | ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId); | ||
| 1491 | |||
| 1492 | // Use the correct engine to detect the package. | ||
| 1493 | switch (pPackage->type) | ||
| 1494 | { | ||
| 1495 | case BURN_PACKAGE_TYPE_EXE: | ||
| 1496 | hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables); | ||
| 1497 | break; | ||
| 1498 | |||
| 1499 | case BURN_PACKAGE_TYPE_MSI: | ||
| 1500 | hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience); | ||
| 1501 | break; | ||
| 1502 | |||
| 1503 | case BURN_PACKAGE_TYPE_MSP: | ||
| 1504 | hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience); | ||
| 1505 | break; | ||
| 1506 | |||
| 1507 | case BURN_PACKAGE_TYPE_MSU: | ||
| 1508 | hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables); | ||
| 1509 | break; | ||
| 1510 | |||
| 1511 | default: | ||
| 1512 | hr = E_NOTIMPL; | ||
| 1513 | ExitOnRootFailure(hr, "Package type not supported by detect yet."); | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | // TODO: consider how to notify the UX that a package is cached. | ||
| 1517 | //else if (BOOTSTRAPPER_PACKAGE_STATE_CACHED > pPackage->currentState && pPackage->fCached) | ||
| 1518 | //{ | ||
| 1519 | // pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_CACHED; | ||
| 1520 | //} | ||
| 1521 | |||
| 1522 | LExit: | ||
| 1523 | if (FAILED(hr)) | ||
| 1524 | { | ||
| 1525 | LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL); | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | if (fBegan) | ||
| 1529 | { | ||
| 1530 | UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState); | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | return hr; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | static HRESULT DetectPackagePayloadsCached( | ||
| 1537 | __in BURN_PACKAGE* pPackage | ||
| 1538 | ) | ||
| 1539 | { | ||
| 1540 | HRESULT hr = S_OK; | ||
| 1541 | LPWSTR sczCachePath = NULL; | ||
| 1542 | BURN_CACHE_STATE cache = BURN_CACHE_STATE_NONE; // assume the package will not be cached. | ||
| 1543 | LPWSTR sczPayloadCachePath = NULL; | ||
| 1544 | LONGLONG llSize = 0; | ||
| 1545 | |||
| 1546 | if (pPackage->sczCacheId && *pPackage->sczCacheId) | ||
| 1547 | { | ||
| 1548 | hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath); | ||
| 1549 | ExitOnFailure(hr, "Failed to get completed cache path."); | ||
| 1550 | |||
| 1551 | // If the cached directory exists, we have something. | ||
| 1552 | if (DirExists(sczCachePath, NULL)) | ||
| 1553 | { | ||
| 1554 | cache = BURN_CACHE_STATE_COMPLETE; // assume all payloads are cached. | ||
| 1555 | |||
| 1556 | // Check all payloads to see if any are missing or not the right size. | ||
| 1557 | for (DWORD i = 0; i < pPackage->cPayloads; ++i) | ||
| 1558 | { | ||
| 1559 | BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i; | ||
| 1560 | |||
| 1561 | hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath); | ||
| 1562 | ExitOnFailure(hr, "Failed to concat payload cache path."); | ||
| 1563 | |||
| 1564 | hr = FileSize(sczPayloadCachePath, &llSize); | ||
| 1565 | if (SUCCEEDED(hr) && static_cast<DWORD64>(llSize) != pPackagePayload->pPayload->qwFileSize) | ||
| 1566 | { | ||
| 1567 | hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); // size did not match expectations, so cache must have the wrong file. | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | if (SUCCEEDED(hr)) | ||
| 1571 | { | ||
| 1572 | // TODO: should we do a full on hash verification on the file to ensure | ||
| 1573 | // the exact right file is cached? | ||
| 1574 | |||
| 1575 | pPackagePayload->fCached = TRUE; | ||
| 1576 | } | ||
| 1577 | else | ||
| 1578 | { | ||
| 1579 | LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey, hr); | ||
| 1580 | |||
| 1581 | cache = BURN_CACHE_STATE_PARTIAL; // found a payload that was not cached so we are partial. | ||
| 1582 | hr = S_OK; | ||
| 1583 | } | ||
| 1584 | } | ||
| 1585 | } | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | pPackage->cache = cache; | ||
| 1589 | |||
| 1590 | LExit: | ||
| 1591 | ReleaseStr(sczPayloadCachePath); | ||
| 1592 | ReleaseStr(sczCachePath); | ||
| 1593 | return hr; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | static DWORD WINAPI CacheThreadProc( | ||
| 1597 | __in LPVOID lpThreadParameter | ||
| 1598 | ) | ||
| 1599 | { | ||
| 1600 | HRESULT hr = S_OK; | ||
| 1601 | BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter); | ||
| 1602 | BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; | ||
| 1603 | DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks; | ||
| 1604 | BOOL* pfRollback = pContext->pfRollback; | ||
| 1605 | BOOL fComInitialized = FALSE; | ||
| 1606 | |||
| 1607 | // initialize COM | ||
| 1608 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 1609 | ExitOnFailure(hr, "Failed to initialize COM on cache thread."); | ||
| 1610 | fComInitialized = TRUE; | ||
| 1611 | |||
| 1612 | // cache packages | ||
| 1613 | hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback); | ||
| 1614 | |||
| 1615 | LExit: | ||
| 1616 | UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. | ||
| 1617 | |||
| 1618 | if (fComInitialized) | ||
| 1619 | { | ||
| 1620 | ::CoUninitialize(); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | return (DWORD)hr; | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | static HRESULT WaitForCacheThread( | ||
| 1627 | __in HANDLE hCacheThread | ||
| 1628 | ) | ||
| 1629 | { | ||
| 1630 | HRESULT hr = S_OK; | ||
| 1631 | |||
| 1632 | if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE)) | ||
| 1633 | { | ||
| 1634 | ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr)) | ||
| 1638 | { | ||
| 1639 | ExitWithLastError(hr, "Failed to get cache thread exit code."); | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | LExit: | ||
| 1643 | return hr; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | static void LogPackages( | ||
| 1647 | __in_opt const BURN_PACKAGE* pUpgradeBundlePackage, | ||
| 1648 | __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage, | ||
| 1649 | __in const BURN_PACKAGES* pPackages, | ||
| 1650 | __in const BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 1651 | __in const BOOTSTRAPPER_ACTION action | ||
| 1652 | ) | ||
| 1653 | { | ||
| 1654 | if (pUpgradeBundlePackage) | ||
| 1655 | { | ||
| 1656 | LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute)); | ||
| 1657 | } | ||
| 1658 | else if (pForwardCompatibleBundlePackage) | ||
| 1659 | { | ||
| 1660 | LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute)); | ||
| 1661 | } | ||
| 1662 | else | ||
| 1663 | { | ||
| 1664 | // Display related bundles first if uninstalling. | ||
| 1665 | if (BOOTSTRAPPER_ACTION_UNINSTALL == action && 0 < pRelatedBundles->cRelatedBundles) | ||
| 1666 | { | ||
| 1667 | for (int i = pRelatedBundles->cRelatedBundles - 1; 0 <= i; --i) | ||
| 1668 | { | ||
| 1669 | const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; | ||
| 1670 | const BURN_PACKAGE* pPackage = &pRelatedBundle->package; | ||
| 1671 | |||
| 1672 | LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); | ||
| 1673 | } | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | // Display all the packages in the log. | ||
| 1677 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 1678 | { | ||
| 1679 | const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i; | ||
| 1680 | const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage]; | ||
| 1681 | |||
| 1682 | LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); | ||
| 1683 | } | ||
| 1684 | |||
| 1685 | for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) | ||
| 1686 | { | ||
| 1687 | const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cCompatiblePackages - 1 - i : i; | ||
| 1688 | const BURN_PACKAGE* pPackage = &pPackages->rgCompatiblePackages[iPackage]; | ||
| 1689 | |||
| 1690 | LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute)); | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | // Display related bundles last if caching, installing, modifying, or repairing. | ||
| 1694 | if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles) | ||
| 1695 | { | ||
| 1696 | for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) | ||
| 1697 | { | ||
| 1698 | const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i]; | ||
| 1699 | const BURN_PACKAGE* pPackage = &pRelatedBundle->package; | ||
| 1700 | |||
| 1701 | LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); | ||
| 1702 | } | ||
| 1703 | } | ||
| 1704 | } | ||
| 1705 | } | ||
diff --git a/src/engine/core.h b/src/engine/core.h new file mode 100644 index 00000000..6a6da2b1 --- /dev/null +++ b/src/engine/core.h | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; | ||
| 13 | |||
| 14 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; | ||
| 15 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; | ||
| 16 | const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; | ||
| 17 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; | ||
| 18 | const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; | ||
| 19 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; | ||
| 20 | const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; | ||
| 21 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; | ||
| 22 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; | ||
| 23 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; | ||
| 24 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; | ||
| 25 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; | ||
| 26 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; | ||
| 27 | const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; | ||
| 28 | const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; | ||
| 29 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; | ||
| 30 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; | ||
| 31 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; | ||
| 32 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; | ||
| 33 | |||
| 34 | const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; | ||
| 35 | const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; | ||
| 36 | const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; | ||
| 37 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; | ||
| 38 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; | ||
| 39 | const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; | ||
| 40 | const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; | ||
| 41 | const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; | ||
| 42 | const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; | ||
| 43 | const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; | ||
| 44 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; | ||
| 45 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; | ||
| 46 | const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; | ||
| 47 | const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; | ||
| 48 | const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; | ||
| 49 | |||
| 50 | // The following constants must stay in sync with src\wix\Binder.cs | ||
| 51 | const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; | ||
| 52 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; | ||
| 53 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; | ||
| 54 | const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; | ||
| 55 | |||
| 56 | |||
| 57 | // enums | ||
| 58 | |||
| 59 | enum BURN_MODE | ||
| 60 | { | ||
| 61 | BURN_MODE_UNTRUSTED, | ||
| 62 | BURN_MODE_NORMAL, | ||
| 63 | BURN_MODE_ELEVATED, | ||
| 64 | BURN_MODE_EMBEDDED, | ||
| 65 | BURN_MODE_RUNONCE, | ||
| 66 | }; | ||
| 67 | |||
| 68 | enum BURN_AU_PAUSE_ACTION | ||
| 69 | { | ||
| 70 | BURN_AU_PAUSE_ACTION_NONE, | ||
| 71 | BURN_AU_PAUSE_ACTION_IFELEVATED, | ||
| 72 | BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME, | ||
| 73 | }; | ||
| 74 | |||
| 75 | |||
| 76 | // structs | ||
| 77 | |||
| 78 | typedef struct _BURN_ENGINE_STATE | ||
| 79 | { | ||
| 80 | // synchronization | ||
| 81 | CRITICAL_SECTION csActive; // Any call from the UX that reads or alters the engine state | ||
| 82 | // needs to be syncronized through this critical section. | ||
| 83 | // Note: The engine must never do a UX callback while in this critical section. | ||
| 84 | |||
| 85 | // UX flow control | ||
| 86 | //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core. | ||
| 87 | //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core. | ||
| 88 | //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods. | ||
| 89 | //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried(). | ||
| 90 | BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply(). | ||
| 91 | |||
| 92 | // engine data | ||
| 93 | BOOTSTRAPPER_COMMAND command; | ||
| 94 | BURN_SECTION section; | ||
| 95 | BURN_VARIABLES variables; | ||
| 96 | BURN_CONDITION condition; | ||
| 97 | BURN_SEARCHES searches; | ||
| 98 | BURN_USER_EXPERIENCE userExperience; | ||
| 99 | BURN_REGISTRATION registration; | ||
| 100 | BURN_CONTAINERS containers; | ||
| 101 | BURN_CATALOGS catalogs; | ||
| 102 | BURN_PAYLOADS payloads; | ||
| 103 | BURN_PACKAGES packages; | ||
| 104 | BURN_UPDATE update; | ||
| 105 | BURN_APPROVED_EXES approvedExes; | ||
| 106 | |||
| 107 | HWND hMessageWindow; | ||
| 108 | HANDLE hMessageWindowThread; | ||
| 109 | |||
| 110 | BOOL fDisableRollback; | ||
| 111 | BOOL fDisableSystemRestore; | ||
| 112 | BOOL fParallelCacheAndExecute; | ||
| 113 | |||
| 114 | BURN_LOGGING log; | ||
| 115 | |||
| 116 | BURN_PLAN plan; | ||
| 117 | |||
| 118 | BURN_MODE mode; | ||
| 119 | BURN_AU_PAUSE_ACTION automaticUpdates; | ||
| 120 | |||
| 121 | DWORD dwElevatedLoggingTlsId; | ||
| 122 | |||
| 123 | LPWSTR sczBundleEngineWorkingPath; | ||
| 124 | BURN_PIPE_CONNECTION companionConnection; | ||
| 125 | BURN_PIPE_CONNECTION embeddedConnection; | ||
| 126 | |||
| 127 | BURN_RESUME_MODE resumeMode; | ||
| 128 | BOOL fDisableUnelevate; | ||
| 129 | |||
| 130 | LPWSTR sczIgnoreDependencies; | ||
| 131 | |||
| 132 | int argc; | ||
| 133 | LPWSTR* argv; | ||
| 134 | } BURN_ENGINE_STATE; | ||
| 135 | |||
| 136 | |||
| 137 | // function declarations | ||
| 138 | |||
| 139 | HRESULT CoreInitialize( | ||
| 140 | __in BURN_ENGINE_STATE* pEngineState | ||
| 141 | ); | ||
| 142 | HRESULT CoreSerializeEngineState( | ||
| 143 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 144 | __inout BYTE** ppbBuffer, | ||
| 145 | __inout SIZE_T* piBuffer | ||
| 146 | ); | ||
| 147 | HRESULT CoreQueryRegistration( | ||
| 148 | __in BURN_ENGINE_STATE* pEngineState | ||
| 149 | ); | ||
| 150 | //HRESULT CoreDeserializeEngineState( | ||
| 151 | // __in BURN_ENGINE_STATE* pEngineState, | ||
| 152 | // __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 153 | // __in SIZE_T cbBuffer | ||
| 154 | // ); | ||
| 155 | HRESULT CoreDetect( | ||
| 156 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 157 | __in_opt HWND hwndParent | ||
| 158 | ); | ||
| 159 | HRESULT CorePlan( | ||
| 160 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 161 | __in BOOTSTRAPPER_ACTION action | ||
| 162 | ); | ||
| 163 | HRESULT CoreElevate( | ||
| 164 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 165 | __in_opt HWND hwndParent | ||
| 166 | ); | ||
| 167 | HRESULT CoreApply( | ||
| 168 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 169 | __in_opt HWND hwndParent | ||
| 170 | ); | ||
| 171 | HRESULT CoreLaunchApprovedExe( | ||
| 172 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 173 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
| 174 | ); | ||
| 175 | HRESULT CoreQuit( | ||
| 176 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 177 | __in int nExitCode | ||
| 178 | ); | ||
| 179 | HRESULT CoreSaveEngineState( | ||
| 180 | __in BURN_ENGINE_STATE* pEngineState | ||
| 181 | ); | ||
| 182 | LPCWSTR CoreRelationTypeToCommandLineString( | ||
| 183 | __in BOOTSTRAPPER_RELATION_TYPE relationType | ||
| 184 | ); | ||
| 185 | HRESULT CoreRecreateCommandLine( | ||
| 186 | __deref_inout_z LPWSTR* psczCommandLine, | ||
| 187 | __in BOOTSTRAPPER_ACTION action, | ||
| 188 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 189 | __in BOOTSTRAPPER_RESTART restart, | ||
| 190 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 191 | __in BOOL fPassthrough, | ||
| 192 | __in_z_opt LPCWSTR wzActiveParent, | ||
| 193 | __in_z_opt LPCWSTR wzAncestors, | ||
| 194 | __in_z_opt LPCWSTR wzAppendLogPath, | ||
| 195 | __in_z_opt LPCWSTR wzAdditionalCommandLineArguments | ||
| 196 | ); | ||
| 197 | HRESULT CoreAppendFileHandleAttachedToCommandLine( | ||
| 198 | __in HANDLE hFileWithAttachedContainer, | ||
| 199 | __out HANDLE* phExecutableFile, | ||
| 200 | __deref_inout_z LPWSTR* psczCommandLine | ||
| 201 | ); | ||
| 202 | HRESULT CoreAppendFileHandleSelfToCommandLine( | ||
| 203 | __in LPCWSTR wzExecutablePath, | ||
| 204 | __out HANDLE* phExecutableFile, | ||
| 205 | __deref_inout_z LPWSTR* psczCommandLine, | ||
| 206 | __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine | ||
| 207 | ); | ||
| 208 | |||
| 209 | #if defined(__cplusplus) | ||
| 210 | } | ||
| 211 | #endif | ||
diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp new file mode 100644 index 00000000..c7c6e024 --- /dev/null +++ b/src/engine/dependency.cpp | |||
| @@ -0,0 +1,1203 @@ | |||
| 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 | |||
| 5 | // constants | ||
| 6 | |||
| 7 | #define INITIAL_STRINGDICT_SIZE 48 | ||
| 8 | const LPCWSTR vcszIgnoreDependenciesDelim = L";"; | ||
| 9 | |||
| 10 | |||
| 11 | // internal function declarations | ||
| 12 | |||
| 13 | static HRESULT SplitIgnoreDependencies( | ||
| 14 | __in_z LPCWSTR wzIgnoreDependencies, | ||
| 15 | __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, | ||
| 16 | __inout LPUINT pcDependencies | ||
| 17 | ); | ||
| 18 | |||
| 19 | static HRESULT JoinIgnoreDependencies( | ||
| 20 | __out_z LPWSTR* psczIgnoreDependencies, | ||
| 21 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
| 22 | __in UINT cDependencies | ||
| 23 | ); | ||
| 24 | |||
| 25 | static HRESULT GetIgnoredDependents( | ||
| 26 | __in const BURN_PACKAGE* pPackage, | ||
| 27 | __in const BURN_PLAN* pPlan, | ||
| 28 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
| 29 | ); | ||
| 30 | |||
| 31 | static HRESULT GetProviderInformation( | ||
| 32 | __in HKEY hkRoot, | ||
| 33 | __in_z LPCWSTR wzProviderKey, | ||
| 34 | __deref_opt_out_z_opt LPWSTR* psczProviderKey, | ||
| 35 | __deref_opt_out_z_opt LPWSTR* psczId | ||
| 36 | ); | ||
| 37 | |||
| 38 | static void CalculateDependencyActionStates( | ||
| 39 | __in const BURN_PACKAGE* pPackage, | ||
| 40 | __in const BOOTSTRAPPER_ACTION action, | ||
| 41 | __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, | ||
| 42 | __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction | ||
| 43 | ); | ||
| 44 | |||
| 45 | static HRESULT AddPackageDependencyActions( | ||
| 46 | __in_opt DWORD *pdwInsertSequence, | ||
| 47 | __in const BURN_PACKAGE* pPackage, | ||
| 48 | __in BURN_PLAN* pPlan, | ||
| 49 | __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, | ||
| 50 | __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction | ||
| 51 | ); | ||
| 52 | |||
| 53 | static HRESULT RegisterPackageProvider( | ||
| 54 | __in const BURN_PACKAGE* pPackage | ||
| 55 | ); | ||
| 56 | |||
| 57 | static void UnregisterPackageProvider( | ||
| 58 | __in const BURN_PACKAGE* pPackage | ||
| 59 | ); | ||
| 60 | |||
| 61 | static HRESULT RegisterPackageDependency( | ||
| 62 | __in BOOL fPerMachine, | ||
| 63 | __in const BURN_PACKAGE* pPackage, | ||
| 64 | __in_z LPCWSTR wzDependentProviderKey | ||
| 65 | ); | ||
| 66 | |||
| 67 | static void UnregisterPackageDependency( | ||
| 68 | __in BOOL fPerMachine, | ||
| 69 | __in const BURN_PACKAGE* pPackage, | ||
| 70 | __in_z LPCWSTR wzDependentProviderKey | ||
| 71 | ); | ||
| 72 | |||
| 73 | static BOOL PackageProviderExists( | ||
| 74 | __in const BURN_PACKAGE* pPackage | ||
| 75 | ); | ||
| 76 | |||
| 77 | |||
| 78 | // functions | ||
| 79 | |||
| 80 | extern "C" void DependencyUninitialize( | ||
| 81 | __in BURN_DEPENDENCY_PROVIDER* pProvider | ||
| 82 | ) | ||
| 83 | { | ||
| 84 | ReleaseStr(pProvider->sczKey); | ||
| 85 | ReleaseStr(pProvider->sczVersion); | ||
| 86 | ReleaseStr(pProvider->sczDisplayName); | ||
| 87 | memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER)); | ||
| 88 | } | ||
| 89 | |||
| 90 | extern "C" HRESULT DependencyParseProvidersFromXml( | ||
| 91 | __in BURN_PACKAGE* pPackage, | ||
| 92 | __in IXMLDOMNode* pixnPackage | ||
| 93 | ) | ||
| 94 | { | ||
| 95 | HRESULT hr = S_OK; | ||
| 96 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 97 | DWORD cNodes = 0; | ||
| 98 | IXMLDOMNode* pixnNode = NULL; | ||
| 99 | |||
| 100 | // Select dependency provider nodes. | ||
| 101 | hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes); | ||
| 102 | ExitOnFailure(hr, "Failed to select dependency provider nodes."); | ||
| 103 | |||
| 104 | // Get dependency provider node count. | ||
| 105 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 106 | ExitOnFailure(hr, "Failed to get the dependency provider node count."); | ||
| 107 | |||
| 108 | if (!cNodes) | ||
| 109 | { | ||
| 110 | ExitFunction1(hr = S_OK); | ||
| 111 | } | ||
| 112 | |||
| 113 | // Allocate memory for dependency provider pointers. | ||
| 114 | pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE); | ||
| 115 | ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); | ||
| 116 | |||
| 117 | pPackage->cDependencyProviders = cNodes; | ||
| 118 | |||
| 119 | // Parse dependency provider elements. | ||
| 120 | for (DWORD i = 0; i < cNodes; i++) | ||
| 121 | { | ||
| 122 | BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i]; | ||
| 123 | |||
| 124 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 125 | ExitOnFailure(hr, "Failed to get the next dependency provider node."); | ||
| 126 | |||
| 127 | // @Key | ||
| 128 | hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey); | ||
| 129 | ExitOnFailure(hr, "Failed to get the Key attribute."); | ||
| 130 | |||
| 131 | // @Version | ||
| 132 | hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion); | ||
| 133 | if (E_NOTFOUND != hr) | ||
| 134 | { | ||
| 135 | ExitOnFailure(hr, "Failed to get the Version attribute."); | ||
| 136 | } | ||
| 137 | |||
| 138 | // @DisplayName | ||
| 139 | hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName); | ||
| 140 | if (E_NOTFOUND != hr) | ||
| 141 | { | ||
| 142 | ExitOnFailure(hr, "Failed to get the DisplayName attribute."); | ||
| 143 | } | ||
| 144 | |||
| 145 | // @Imported | ||
| 146 | hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported); | ||
| 147 | if (E_NOTFOUND != hr) | ||
| 148 | { | ||
| 149 | ExitOnFailure(hr, "Failed to get the Imported attribute."); | ||
| 150 | } | ||
| 151 | else | ||
| 152 | { | ||
| 153 | pDependencyProvider->fImported = FALSE; | ||
| 154 | hr = S_OK; | ||
| 155 | } | ||
| 156 | |||
| 157 | // Prepare next iteration. | ||
| 158 | ReleaseNullObject(pixnNode); | ||
| 159 | } | ||
| 160 | |||
| 161 | hr = S_OK; | ||
| 162 | |||
| 163 | LExit: | ||
| 164 | ReleaseObject(pixnNode); | ||
| 165 | ReleaseObject(pixnNodes); | ||
| 166 | |||
| 167 | return hr; | ||
| 168 | } | ||
| 169 | |||
| 170 | extern "C" HRESULT DependencyDetectProviderKeyPackageId( | ||
| 171 | __in const BURN_PACKAGE* pPackage, | ||
| 172 | __deref_opt_out_z_opt LPWSTR* psczProviderKey, | ||
| 173 | __deref_opt_out_z_opt LPWSTR* psczId | ||
| 174 | ) | ||
| 175 | { | ||
| 176 | HRESULT hr = E_NOTFOUND; | ||
| 177 | LPWSTR wzProviderKey = NULL; | ||
| 178 | HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 179 | |||
| 180 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 181 | { | ||
| 182 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 183 | |||
| 184 | // Find the first package id registered for the provider key. | ||
| 185 | hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId); | ||
| 186 | if (E_NOTFOUND == hr) | ||
| 187 | { | ||
| 188 | continue; | ||
| 189 | } | ||
| 190 | ExitOnFailure(hr, "Failed to get the package provider information."); | ||
| 191 | |||
| 192 | ExitFunction(); | ||
| 193 | } | ||
| 194 | |||
| 195 | // Older bundles may not have written the id so try the default. | ||
| 196 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type) | ||
| 197 | { | ||
| 198 | wzProviderKey = pPackage->Msi.sczProductCode; | ||
| 199 | } | ||
| 200 | |||
| 201 | if (wzProviderKey) | ||
| 202 | { | ||
| 203 | hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId); | ||
| 204 | if (E_NOTFOUND == hr) | ||
| 205 | { | ||
| 206 | ExitFunction(); | ||
| 207 | } | ||
| 208 | ExitOnFailure(hr, "Failed to get the package default provider information."); | ||
| 209 | } | ||
| 210 | |||
| 211 | LExit: | ||
| 212 | return hr; | ||
| 213 | } | ||
| 214 | |||
| 215 | extern "C" HRESULT DependencyDetectProviderKeyBundleId( | ||
| 216 | __in BURN_REGISTRATION* pRegistration | ||
| 217 | ) | ||
| 218 | { | ||
| 219 | HRESULT hr = S_OK; | ||
| 220 | |||
| 221 | hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL); | ||
| 222 | if (E_NOTFOUND == hr) | ||
| 223 | { | ||
| 224 | ExitFunction(); | ||
| 225 | } | ||
| 226 | ExitOnFailure(hr, "Failed to get provider key bundle id."); | ||
| 227 | |||
| 228 | // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key. | ||
| 229 | if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId) | ||
| 230 | { | ||
| 231 | hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0); | ||
| 232 | ExitOnFailure(hr, "Failed to initialize provider key bundle id."); | ||
| 233 | } | ||
| 234 | |||
| 235 | LExit: | ||
| 236 | return hr; | ||
| 237 | } | ||
| 238 | |||
| 239 | extern "C" HRESULT DependencyPlanInitialize( | ||
| 240 | __in const BURN_ENGINE_STATE* pEngineState, | ||
| 241 | __in BURN_PLAN* pPlan | ||
| 242 | ) | ||
| 243 | { | ||
| 244 | HRESULT hr = S_OK; | ||
| 245 | |||
| 246 | // The current bundle provider key should always be ignored for dependency checks. | ||
| 247 | hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL); | ||
| 248 | ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); | ||
| 249 | |||
| 250 | // Add the list of dependencies to ignore to the plan. | ||
| 251 | if (pEngineState->sczIgnoreDependencies) | ||
| 252 | { | ||
| 253 | // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere. | ||
| 254 | hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders); | ||
| 255 | ExitOnFailure(hr, "Failed to split the list of dependencies to ignore."); | ||
| 256 | } | ||
| 257 | |||
| 258 | LExit: | ||
| 259 | return hr; | ||
| 260 | } | ||
| 261 | |||
| 262 | extern "C" HRESULT DependencyAllocIgnoreDependencies( | ||
| 263 | __in const BURN_PLAN *pPlan, | ||
| 264 | __out_z LPWSTR* psczIgnoreDependencies | ||
| 265 | ) | ||
| 266 | { | ||
| 267 | HRESULT hr = S_OK; | ||
| 268 | |||
| 269 | // Join the list of dependencies to ignore for each related bundle. | ||
| 270 | if (0 < pPlan->cPlannedProviders) | ||
| 271 | { | ||
| 272 | hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders); | ||
| 273 | ExitOnFailure(hr, "Failed to join the list of dependencies to ignore."); | ||
| 274 | } | ||
| 275 | |||
| 276 | LExit: | ||
| 277 | return hr; | ||
| 278 | } | ||
| 279 | |||
| 280 | extern "C" HRESULT DependencyAddIgnoreDependencies( | ||
| 281 | __in STRINGDICT_HANDLE sdIgnoreDependencies, | ||
| 282 | __in_z LPCWSTR wzAddIgnoreDependencies | ||
| 283 | ) | ||
| 284 | { | ||
| 285 | HRESULT hr = S_OK; | ||
| 286 | LPWSTR wzContext = NULL; | ||
| 287 | |||
| 288 | // Parse through the semicolon-delimited tokens and add to the array. | ||
| 289 | for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) | ||
| 290 | { | ||
| 291 | hr = DictKeyExists(sdIgnoreDependencies, wzToken); | ||
| 292 | if (E_NOTFOUND != hr) | ||
| 293 | { | ||
| 294 | ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); | ||
| 295 | } | ||
| 296 | else | ||
| 297 | { | ||
| 298 | hr = DictAddKey(sdIgnoreDependencies, wzToken); | ||
| 299 | ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | LExit: | ||
| 304 | return hr; | ||
| 305 | } | ||
| 306 | |||
| 307 | extern "C" BOOL DependencyDependentExists( | ||
| 308 | __in const BURN_REGISTRATION* pRegistration, | ||
| 309 | __in_z LPCWSTR wzDependentProviderKey | ||
| 310 | ) | ||
| 311 | { | ||
| 312 | HRESULT hr = S_OK; | ||
| 313 | |||
| 314 | hr = DepDependentExists(pRegistration->hkRoot, pRegistration->sczProviderKey, wzDependentProviderKey); | ||
| 315 | return SUCCEEDED(hr); | ||
| 316 | } | ||
| 317 | |||
| 318 | extern "C" HRESULT DependencyPlanPackageBegin( | ||
| 319 | __in BOOL fPerMachine, | ||
| 320 | __in BURN_PACKAGE* pPackage, | ||
| 321 | __in BURN_PLAN* pPlan | ||
| 322 | ) | ||
| 323 | { | ||
| 324 | HRESULT hr = S_OK; | ||
| 325 | STRINGDICT_HANDLE sdIgnoredDependents = NULL; | ||
| 326 | DEPENDENCY* rgDependents = NULL; | ||
| 327 | UINT cDependents = 0; | ||
| 328 | HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 329 | BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; | ||
| 330 | BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; | ||
| 331 | |||
| 332 | pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; | ||
| 333 | pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; | ||
| 334 | |||
| 335 | // Make sure the package defines at least one provider. | ||
| 336 | if (0 == pPackage->cDependencyProviders) | ||
| 337 | { | ||
| 338 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); | ||
| 339 | ExitFunction1(hr = S_OK); | ||
| 340 | } | ||
| 341 | |||
| 342 | // Make sure the package is in the same scope as the bundle. | ||
| 343 | if (fPerMachine != pPackage->fPerMachine) | ||
| 344 | { | ||
| 345 | LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); | ||
| 346 | ExitFunction1(hr = S_OK); | ||
| 347 | } | ||
| 348 | |||
| 349 | // If we're uninstalling the package, check if any dependents are registered. | ||
| 350 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) | ||
| 351 | { | ||
| 352 | // Build up a list of dependents to ignore, including the current bundle. | ||
| 353 | hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); | ||
| 354 | ExitOnFailure(hr, "Failed to build the list of ignored dependents."); | ||
| 355 | |||
| 356 | // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. | ||
| 357 | hr = DictKeyExists(sdIgnoredDependents, L"ALL"); | ||
| 358 | if (E_NOTFOUND != hr) | ||
| 359 | { | ||
| 360 | ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); | ||
| 361 | } | ||
| 362 | else | ||
| 363 | { | ||
| 364 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 365 | { | ||
| 366 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 367 | |||
| 368 | hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); | ||
| 369 | if (E_FILENOTFOUND != hr) | ||
| 370 | { | ||
| 371 | ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); | ||
| 372 | } | ||
| 373 | else | ||
| 374 | { | ||
| 375 | hr = S_OK; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | // Calculate the dependency actions before the package itself is planned. | ||
| 382 | CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); | ||
| 383 | |||
| 384 | // If dependents were found, change the action to not uninstall the package. | ||
| 385 | if (0 < cDependents) | ||
| 386 | { | ||
| 387 | LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); | ||
| 388 | |||
| 389 | for (DWORD i = 0; i < cDependents; ++i) | ||
| 390 | { | ||
| 391 | const DEPENDENCY* pDependency = &rgDependents[i]; | ||
| 392 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); | ||
| 393 | } | ||
| 394 | |||
| 395 | pPackage->fDependencyManagerWasHere = TRUE; | ||
| 396 | pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 397 | pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 398 | } | ||
| 399 | // Use the calculated dependency actions as the provider actions if there | ||
| 400 | // are any non-imported providers that need to be registered and the package | ||
| 401 | // is current (not obsolete). | ||
| 402 | else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState) | ||
| 403 | { | ||
| 404 | BOOL fAllImportedProviders = TRUE; // assume all providers were imported. | ||
| 405 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 406 | { | ||
| 407 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 408 | if (!pProvider->fImported) | ||
| 409 | { | ||
| 410 | fAllImportedProviders = FALSE; | ||
| 411 | break; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | if (!fAllImportedProviders) | ||
| 416 | { | ||
| 417 | pPackage->providerExecute = dependencyExecuteAction; | ||
| 418 | pPackage->providerRollback = dependencyRollbackAction; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | // If the package will be removed, add its providers to the growing list in the plan. | ||
| 423 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) | ||
| 424 | { | ||
| 425 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 426 | { | ||
| 427 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 428 | |||
| 429 | hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); | ||
| 430 | ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | pPackage->dependencyExecute = dependencyExecuteAction; | ||
| 435 | pPackage->dependencyRollback = dependencyRollbackAction; | ||
| 436 | |||
| 437 | LExit: | ||
| 438 | ReleaseDependencyArray(rgDependents, cDependents); | ||
| 439 | ReleaseDict(sdIgnoredDependents); | ||
| 440 | |||
| 441 | return hr; | ||
| 442 | } | ||
| 443 | |||
| 444 | extern "C" HRESULT DependencyPlanPackage( | ||
| 445 | __in_opt DWORD *pdwInsertSequence, | ||
| 446 | __in const BURN_PACKAGE* pPackage, | ||
| 447 | __in BURN_PLAN* pPlan | ||
| 448 | ) | ||
| 449 | { | ||
| 450 | HRESULT hr = S_OK; | ||
| 451 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 452 | |||
| 453 | // If the dependency execution action is to unregister, add the dependency actions to the plan | ||
| 454 | // *before* the provider key is potentially removed. | ||
| 455 | if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute) | ||
| 456 | { | ||
| 457 | hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); | ||
| 458 | ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); | ||
| 459 | } | ||
| 460 | |||
| 461 | // Add the provider rollback plan. | ||
| 462 | if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback) | ||
| 463 | { | ||
| 464 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 465 | ExitOnFailure(hr, "Failed to append provider rollback action."); | ||
| 466 | |||
| 467 | pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; | ||
| 468 | pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage); | ||
| 469 | pAction->packageProvider.action = pPackage->providerRollback; | ||
| 470 | |||
| 471 | // Put a checkpoint before the execute action so that rollback happens | ||
| 472 | // if execute fails. | ||
| 473 | hr = PlanExecuteCheckpoint(pPlan); | ||
| 474 | ExitOnFailure(hr, "Failed to plan provider checkpoint action."); | ||
| 475 | } | ||
| 476 | |||
| 477 | // Add the provider execute plan. This comes after rollback so if something goes wrong | ||
| 478 | // rollback will try to clean up after us. | ||
| 479 | if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute) | ||
| 480 | { | ||
| 481 | if (NULL != pdwInsertSequence) | ||
| 482 | { | ||
| 483 | hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); | ||
| 484 | ExitOnFailure(hr, "Failed to insert provider execute action."); | ||
| 485 | |||
| 486 | // Always move the sequence after this dependency action so the provider registration | ||
| 487 | // stays in front of the inserted actions. | ||
| 488 | ++(*pdwInsertSequence); | ||
| 489 | } | ||
| 490 | else | ||
| 491 | { | ||
| 492 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 493 | ExitOnFailure(hr, "Failed to append provider execute action."); | ||
| 494 | } | ||
| 495 | |||
| 496 | pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; | ||
| 497 | pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage); | ||
| 498 | pAction->packageProvider.action = pPackage->providerExecute; | ||
| 499 | } | ||
| 500 | |||
| 501 | LExit: | ||
| 502 | return hr; | ||
| 503 | } | ||
| 504 | |||
| 505 | extern "C" HRESULT DependencyPlanPackageComplete( | ||
| 506 | __in BURN_PACKAGE* pPackage, | ||
| 507 | __in BURN_PLAN* pPlan | ||
| 508 | ) | ||
| 509 | { | ||
| 510 | HRESULT hr = S_OK; | ||
| 511 | |||
| 512 | // Registration of dependencies happens here, after the package is planned to be | ||
| 513 | // installed and all that good stuff. | ||
| 514 | if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) | ||
| 515 | { | ||
| 516 | // Recalculate the dependency actions in case other operations may have changed | ||
| 517 | // the package execution state. | ||
| 518 | CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback); | ||
| 519 | |||
| 520 | // If the dependency execution action is *still* to register, add the dependency actions to the plan. | ||
| 521 | if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute) | ||
| 522 | { | ||
| 523 | hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback); | ||
| 524 | ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId); | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | LExit: | ||
| 529 | return hr; | ||
| 530 | } | ||
| 531 | |||
| 532 | extern "C" HRESULT DependencyExecutePackageProviderAction( | ||
| 533 | __in const BURN_EXECUTE_ACTION* pAction | ||
| 534 | ) | ||
| 535 | { | ||
| 536 | AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function."); | ||
| 537 | |||
| 538 | HRESULT hr = S_OK; | ||
| 539 | const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage; | ||
| 540 | |||
| 541 | // Register or unregister the package provider(s). | ||
| 542 | if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action) | ||
| 543 | { | ||
| 544 | hr = RegisterPackageProvider(pPackage); | ||
| 545 | ExitOnFailure(hr, "Failed to register the package providers."); | ||
| 546 | } | ||
| 547 | else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action) | ||
| 548 | { | ||
| 549 | UnregisterPackageProvider(pPackage); | ||
| 550 | } | ||
| 551 | |||
| 552 | LExit: | ||
| 553 | if (!pPackage->fVital) | ||
| 554 | { | ||
| 555 | hr = S_OK; | ||
| 556 | } | ||
| 557 | |||
| 558 | return hr; | ||
| 559 | } | ||
| 560 | |||
| 561 | extern "C" HRESULT DependencyExecutePackageDependencyAction( | ||
| 562 | __in BOOL fPerMachine, | ||
| 563 | __in const BURN_EXECUTE_ACTION* pAction | ||
| 564 | ) | ||
| 565 | { | ||
| 566 | AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function."); | ||
| 567 | |||
| 568 | HRESULT hr = S_OK; | ||
| 569 | const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage; | ||
| 570 | |||
| 571 | // Register or unregister the bundle as a dependent of each package dependency provider. | ||
| 572 | if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action) | ||
| 573 | { | ||
| 574 | hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); | ||
| 575 | ExitOnFailure(hr, "Failed to register the dependency on the package provider."); | ||
| 576 | } | ||
| 577 | else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action) | ||
| 578 | { | ||
| 579 | UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey); | ||
| 580 | } | ||
| 581 | |||
| 582 | LExit: | ||
| 583 | if (!pPackage->fVital) | ||
| 584 | { | ||
| 585 | hr = S_OK; | ||
| 586 | } | ||
| 587 | |||
| 588 | return hr; | ||
| 589 | } | ||
| 590 | |||
| 591 | extern "C" HRESULT DependencyRegisterBundle( | ||
| 592 | __in const BURN_REGISTRATION* pRegistration | ||
| 593 | ) | ||
| 594 | { | ||
| 595 | HRESULT hr = S_OK; | ||
| 596 | LPWSTR sczVersion = NULL; | ||
| 597 | |||
| 598 | hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion); | ||
| 599 | ExitOnFailure(hr, "Failed to format the registration version string."); | ||
| 600 | |||
| 601 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion); | ||
| 602 | |||
| 603 | // Register the bundle provider key. | ||
| 604 | hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); | ||
| 605 | ExitOnFailure(hr, "Failed to register the bundle dependency provider."); | ||
| 606 | |||
| 607 | LExit: | ||
| 608 | ReleaseStr(sczVersion); | ||
| 609 | |||
| 610 | return hr; | ||
| 611 | } | ||
| 612 | |||
| 613 | extern "C" HRESULT DependencyProcessDependentRegistration( | ||
| 614 | __in const BURN_REGISTRATION* pRegistration, | ||
| 615 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 616 | ) | ||
| 617 | { | ||
| 618 | HRESULT hr = S_OK; | ||
| 619 | |||
| 620 | switch (pAction->type) | ||
| 621 | { | ||
| 622 | case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER: | ||
| 623 | hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0); | ||
| 624 | ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey); | ||
| 625 | break; | ||
| 626 | |||
| 627 | case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER: | ||
| 628 | hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey); | ||
| 629 | ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey); | ||
| 630 | break; | ||
| 631 | |||
| 632 | default: | ||
| 633 | hr = E_INVALIDARG; | ||
| 634 | ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type); | ||
| 635 | } | ||
| 636 | |||
| 637 | LExit: | ||
| 638 | return hr; | ||
| 639 | } | ||
| 640 | |||
| 641 | extern "C" void DependencyUnregisterBundle( | ||
| 642 | __in const BURN_REGISTRATION* pRegistration | ||
| 643 | ) | ||
| 644 | { | ||
| 645 | HRESULT hr = S_OK; | ||
| 646 | |||
| 647 | // Remove the bundle provider key. | ||
| 648 | hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); | ||
| 649 | if (SUCCEEDED(hr)) | ||
| 650 | { | ||
| 651 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey); | ||
| 652 | } | ||
| 653 | else if (FAILED(hr) && E_FILENOTFOUND != hr) | ||
| 654 | { | ||
| 655 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); | ||
| 656 | } | ||
| 657 | } | ||
| 658 | |||
| 659 | // internal functions | ||
| 660 | |||
| 661 | /******************************************************************** | ||
| 662 | SplitIgnoreDependencies - Splits a semicolon-delimited | ||
| 663 | string into a list of unique dependencies to ignore. | ||
| 664 | |||
| 665 | *********************************************************************/ | ||
| 666 | static HRESULT SplitIgnoreDependencies( | ||
| 667 | __in_z LPCWSTR wzIgnoreDependencies, | ||
| 668 | __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, | ||
| 669 | __inout LPUINT pcDependencies | ||
| 670 | ) | ||
| 671 | { | ||
| 672 | HRESULT hr = S_OK; | ||
| 673 | LPWSTR wzContext = NULL; | ||
| 674 | STRINGDICT_HANDLE sdIgnoreDependencies = NULL; | ||
| 675 | |||
| 676 | // Create a dictionary to hold unique dependencies. | ||
| 677 | hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
| 678 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
| 679 | |||
| 680 | // Parse through the semicolon-delimited tokens and add to the array. | ||
| 681 | for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) | ||
| 682 | { | ||
| 683 | hr = DictKeyExists(sdIgnoreDependencies, wzToken); | ||
| 684 | if (E_NOTFOUND != hr) | ||
| 685 | { | ||
| 686 | ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); | ||
| 687 | } | ||
| 688 | else | ||
| 689 | { | ||
| 690 | hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL); | ||
| 691 | ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken); | ||
| 692 | |||
| 693 | hr = DictAddKey(sdIgnoreDependencies, wzToken); | ||
| 694 | ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | LExit: | ||
| 699 | ReleaseDict(sdIgnoreDependencies); | ||
| 700 | |||
| 701 | return hr; | ||
| 702 | } | ||
| 703 | |||
| 704 | /******************************************************************** | ||
| 705 | JoinIgnoreDependencies - Joins a list of dependencies | ||
| 706 | to ignore into a semicolon-delimited string of unique values. | ||
| 707 | |||
| 708 | *********************************************************************/ | ||
| 709 | static HRESULT JoinIgnoreDependencies( | ||
| 710 | __out_z LPWSTR* psczIgnoreDependencies, | ||
| 711 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
| 712 | __in UINT cDependencies | ||
| 713 | ) | ||
| 714 | { | ||
| 715 | HRESULT hr = S_OK; | ||
| 716 | STRINGDICT_HANDLE sdIgnoreDependencies = NULL; | ||
| 717 | |||
| 718 | // Make sure we pass back an empty string if there are no dependencies. | ||
| 719 | if (0 == cDependencies) | ||
| 720 | { | ||
| 721 | ExitFunction1(hr = S_OK); | ||
| 722 | } | ||
| 723 | |||
| 724 | // Create a dictionary to hold unique dependencies. | ||
| 725 | hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
| 726 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
| 727 | |||
| 728 | for (UINT i = 0; i < cDependencies; ++i) | ||
| 729 | { | ||
| 730 | const DEPENDENCY* pDependency = &rgDependencies[i]; | ||
| 731 | |||
| 732 | hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey); | ||
| 733 | if (E_NOTFOUND != hr) | ||
| 734 | { | ||
| 735 | ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); | ||
| 736 | } | ||
| 737 | else | ||
| 738 | { | ||
| 739 | if (0 < i) | ||
| 740 | { | ||
| 741 | hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1); | ||
| 742 | ExitOnFailure(hr, "Failed to append the string delimiter."); | ||
| 743 | } | ||
| 744 | |||
| 745 | hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0); | ||
| 746 | ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey); | ||
| 747 | |||
| 748 | hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey); | ||
| 749 | ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey); | ||
| 750 | } | ||
| 751 | } | ||
| 752 | |||
| 753 | LExit: | ||
| 754 | ReleaseDict(sdIgnoreDependencies); | ||
| 755 | |||
| 756 | return hr; | ||
| 757 | } | ||
| 758 | |||
| 759 | /******************************************************************** | ||
| 760 | GetIgnoredDependents - Combines the current bundle's | ||
| 761 | provider key, packages' provider keys that are being uninstalled, | ||
| 762 | and any ignored dependencies authored for packages into a string | ||
| 763 | list to pass to deputil. | ||
| 764 | |||
| 765 | *********************************************************************/ | ||
| 766 | static HRESULT GetIgnoredDependents( | ||
| 767 | __in const BURN_PACKAGE* pPackage, | ||
| 768 | __in const BURN_PLAN* pPlan, | ||
| 769 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
| 770 | ) | ||
| 771 | { | ||
| 772 | HRESULT hr = S_OK; | ||
| 773 | LPWSTR sczIgnoreDependencies = NULL; | ||
| 774 | |||
| 775 | // Create the dictionary and add the bundle provider key initially. | ||
| 776 | hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
| 777 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
| 778 | |||
| 779 | hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey); | ||
| 780 | ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey); | ||
| 781 | |||
| 782 | // Add previously planned package providers to the dictionary. | ||
| 783 | for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) | ||
| 784 | { | ||
| 785 | const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i]; | ||
| 786 | |||
| 787 | hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey); | ||
| 788 | ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey); | ||
| 789 | } | ||
| 790 | |||
| 791 | // Get the IGNOREDEPENDENCIES property if defined. | ||
| 792 | hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies); | ||
| 793 | if (E_NOTFOUND != hr) | ||
| 794 | { | ||
| 795 | ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES); | ||
| 796 | |||
| 797 | hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies); | ||
| 798 | ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies."); | ||
| 799 | } | ||
| 800 | else | ||
| 801 | { | ||
| 802 | hr = S_OK; | ||
| 803 | } | ||
| 804 | |||
| 805 | LExit: | ||
| 806 | ReleaseStr(sczIgnoreDependencies); | ||
| 807 | |||
| 808 | return hr; | ||
| 809 | } | ||
| 810 | |||
| 811 | /******************************************************************** | ||
| 812 | GetProviderId - Gets the ID of the package given the provider key. | ||
| 813 | |||
| 814 | *********************************************************************/ | ||
| 815 | static HRESULT GetProviderInformation( | ||
| 816 | __in HKEY hkRoot, | ||
| 817 | __in_z LPCWSTR wzProviderKey, | ||
| 818 | __deref_opt_out_z_opt LPWSTR* psczProviderKey, | ||
| 819 | __deref_opt_out_z_opt LPWSTR* psczId | ||
| 820 | ) | ||
| 821 | { | ||
| 822 | HRESULT hr = S_OK; | ||
| 823 | LPWSTR sczId = NULL; | ||
| 824 | |||
| 825 | hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL); | ||
| 826 | if (E_NOTFOUND == hr) | ||
| 827 | { | ||
| 828 | ExitFunction(); | ||
| 829 | } | ||
| 830 | ExitOnFailure(hr, "Failed to get the provider key package id."); | ||
| 831 | |||
| 832 | // If the id was registered return it and exit. | ||
| 833 | if (sczId && *sczId) | ||
| 834 | { | ||
| 835 | if (psczProviderKey) | ||
| 836 | { | ||
| 837 | hr = StrAllocString(psczProviderKey, wzProviderKey, 0); | ||
| 838 | ExitOnFailure(hr, "Failed to copy the provider key."); | ||
| 839 | } | ||
| 840 | |||
| 841 | if (psczId) | ||
| 842 | { | ||
| 843 | *psczId = sczId; | ||
| 844 | sczId = NULL; | ||
| 845 | } | ||
| 846 | |||
| 847 | ExitFunction(); | ||
| 848 | } | ||
| 849 | else | ||
| 850 | { | ||
| 851 | hr = E_NOTFOUND; | ||
| 852 | } | ||
| 853 | |||
| 854 | LExit: | ||
| 855 | ReleaseStr(sczId); | ||
| 856 | |||
| 857 | return hr; | ||
| 858 | } | ||
| 859 | |||
| 860 | /******************************************************************** | ||
| 861 | CalculateDependencyActionStates - Calculates the dependency execute and | ||
| 862 | rollback actions for a package. | ||
| 863 | |||
| 864 | *********************************************************************/ | ||
| 865 | static void CalculateDependencyActionStates( | ||
| 866 | __in const BURN_PACKAGE* pPackage, | ||
| 867 | __in const BOOTSTRAPPER_ACTION action, | ||
| 868 | __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction, | ||
| 869 | __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction | ||
| 870 | ) | ||
| 871 | { | ||
| 872 | switch (action) | ||
| 873 | { | ||
| 874 | case BOOTSTRAPPER_ACTION_UNINSTALL: | ||
| 875 | // Always remove the dependency when uninstalling a bundle even if the package is absent. | ||
| 876 | *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; | ||
| 877 | break; | ||
| 878 | case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; | ||
| 879 | case BOOTSTRAPPER_ACTION_CACHE: | ||
| 880 | // Always remove the dependency during rollback when installing a bundle. | ||
| 881 | *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; | ||
| 882 | __fallthrough; | ||
| 883 | case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; | ||
| 884 | case BOOTSTRAPPER_ACTION_REPAIR: | ||
| 885 | switch (pPackage->execute) | ||
| 886 | { | ||
| 887 | case BOOTSTRAPPER_ACTION_STATE_NONE: | ||
| 888 | switch (pPackage->requested) | ||
| 889 | { | ||
| 890 | case BOOTSTRAPPER_REQUEST_STATE_NONE: | ||
| 891 | // Register if a newer, compatible package is already installed. | ||
| 892 | switch (pPackage->currentState) | ||
| 893 | { | ||
| 894 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: | ||
| 895 | if (!PackageProviderExists(pPackage)) | ||
| 896 | { | ||
| 897 | break; | ||
| 898 | } | ||
| 899 | __fallthrough; | ||
| 900 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 901 | *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; | ||
| 902 | break; | ||
| 903 | } | ||
| 904 | break; | ||
| 905 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 906 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 907 | // Register if the package is requested but already installed. | ||
| 908 | switch (pPackage->currentState) | ||
| 909 | { | ||
| 910 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: | ||
| 911 | if (!PackageProviderExists(pPackage)) | ||
| 912 | { | ||
| 913 | break; | ||
| 914 | } | ||
| 915 | __fallthrough; | ||
| 916 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; | ||
| 917 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 918 | *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; | ||
| 919 | break; | ||
| 920 | } | ||
| 921 | break; | ||
| 922 | } | ||
| 923 | break; | ||
| 924 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 925 | *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER; | ||
| 926 | break; | ||
| 927 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; | ||
| 928 | case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; | ||
| 929 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; | ||
| 930 | case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; | ||
| 931 | case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; | ||
| 932 | case BOOTSTRAPPER_ACTION_STATE_PATCH: | ||
| 933 | *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER; | ||
| 934 | break; | ||
| 935 | } | ||
| 936 | break; | ||
| 937 | } | ||
| 938 | |||
| 939 | switch (*pDependencyExecuteAction) | ||
| 940 | { | ||
| 941 | case BURN_DEPENDENCY_ACTION_REGISTER: | ||
| 942 | switch (pPackage->currentState) | ||
| 943 | { | ||
| 944 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; | ||
| 945 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; | ||
| 946 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 947 | *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER; | ||
| 948 | break; | ||
| 949 | } | ||
| 950 | break; | ||
| 951 | case BURN_DEPENDENCY_ACTION_UNREGISTER: | ||
| 952 | switch (pPackage->currentState) | ||
| 953 | { | ||
| 954 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; | ||
| 955 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 956 | *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER; | ||
| 957 | break; | ||
| 958 | } | ||
| 959 | break; | ||
| 960 | } | ||
| 961 | } | ||
| 962 | |||
| 963 | /******************************************************************** | ||
| 964 | AddPackageDependencyActions - Adds the dependency execute and rollback | ||
| 965 | actions to the plan. | ||
| 966 | |||
| 967 | *********************************************************************/ | ||
| 968 | static HRESULT AddPackageDependencyActions( | ||
| 969 | __in_opt DWORD *pdwInsertSequence, | ||
| 970 | __in const BURN_PACKAGE* pPackage, | ||
| 971 | __in BURN_PLAN* pPlan, | ||
| 972 | __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction, | ||
| 973 | __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction | ||
| 974 | ) | ||
| 975 | { | ||
| 976 | HRESULT hr = S_OK; | ||
| 977 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 978 | |||
| 979 | // Add the rollback plan. | ||
| 980 | if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction) | ||
| 981 | { | ||
| 982 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 983 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 984 | |||
| 985 | pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; | ||
| 986 | pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage); | ||
| 987 | pAction->packageDependency.action = dependencyRollbackAction; | ||
| 988 | |||
| 989 | hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); | ||
| 990 | ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); | ||
| 991 | |||
| 992 | // Put a checkpoint before the execute action so that rollback happens | ||
| 993 | // if execute fails. | ||
| 994 | hr = PlanExecuteCheckpoint(pPlan); | ||
| 995 | ExitOnFailure(hr, "Failed to plan dependency checkpoint action."); | ||
| 996 | } | ||
| 997 | |||
| 998 | // Add the execute plan. This comes after rollback so if something goes wrong | ||
| 999 | // rollback will try to clean up after us correctly. | ||
| 1000 | if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction) | ||
| 1001 | { | ||
| 1002 | if (NULL != pdwInsertSequence) | ||
| 1003 | { | ||
| 1004 | hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); | ||
| 1005 | ExitOnFailure(hr, "Failed to insert execute action."); | ||
| 1006 | |||
| 1007 | // Always move the sequence after this dependency action so the dependency registration | ||
| 1008 | // stays in front of the inserted actions. | ||
| 1009 | ++(*pdwInsertSequence); | ||
| 1010 | } | ||
| 1011 | else | ||
| 1012 | { | ||
| 1013 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 1014 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; | ||
| 1018 | pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage); | ||
| 1019 | pAction->packageDependency.action = dependencyExecuteAction; | ||
| 1020 | |||
| 1021 | hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0); | ||
| 1022 | ExitOnFailure(hr, "Failed to copy the bundle dependency provider."); | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | LExit: | ||
| 1026 | return hr; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | static HRESULT RegisterPackageProvider( | ||
| 1030 | __in const BURN_PACKAGE* pPackage | ||
| 1031 | ) | ||
| 1032 | { | ||
| 1033 | HRESULT hr = S_OK; | ||
| 1034 | LPWSTR wzId = NULL; | ||
| 1035 | HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 1036 | |||
| 1037 | if (pPackage->rgDependencyProviders) | ||
| 1038 | { | ||
| 1039 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type) | ||
| 1040 | { | ||
| 1041 | wzId = pPackage->Msi.sczProductCode; | ||
| 1042 | } | ||
| 1043 | else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 1044 | { | ||
| 1045 | wzId = pPackage->Msp.sczPatchCode; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 1049 | { | ||
| 1050 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 1051 | |||
| 1052 | if (!pProvider->fImported) | ||
| 1053 | { | ||
| 1054 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId); | ||
| 1055 | |||
| 1056 | hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0); | ||
| 1057 | ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey); | ||
| 1058 | } | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | LExit: | ||
| 1063 | if (!pPackage->fVital) | ||
| 1064 | { | ||
| 1065 | hr = S_OK; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | return hr; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /******************************************************************** | ||
| 1072 | UnregisterPackageProvider - Removes each dependency provider | ||
| 1073 | for the package (if not imported from the package itself). | ||
| 1074 | |||
| 1075 | Note: Does not check for existing dependents before removing the key. | ||
| 1076 | *********************************************************************/ | ||
| 1077 | static void UnregisterPackageProvider( | ||
| 1078 | __in const BURN_PACKAGE* pPackage | ||
| 1079 | ) | ||
| 1080 | { | ||
| 1081 | HRESULT hr = S_OK; | ||
| 1082 | HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 1083 | |||
| 1084 | if (pPackage->rgDependencyProviders) | ||
| 1085 | { | ||
| 1086 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 1087 | { | ||
| 1088 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 1089 | |||
| 1090 | if (!pProvider->fImported) | ||
| 1091 | { | ||
| 1092 | hr = DepUnregisterDependency(hkRoot, pProvider->sczKey); | ||
| 1093 | if (SUCCEEDED(hr)) | ||
| 1094 | { | ||
| 1095 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId); | ||
| 1096 | } | ||
| 1097 | else if (FAILED(hr) && E_FILENOTFOUND != hr) | ||
| 1098 | { | ||
| 1099 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | } | ||
| 1103 | } | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | /******************************************************************** | ||
| 1107 | RegisterPackageDependency - Registers the provider key | ||
| 1108 | as a dependent of a package. | ||
| 1109 | |||
| 1110 | *********************************************************************/ | ||
| 1111 | static HRESULT RegisterPackageDependency( | ||
| 1112 | __in BOOL fPerMachine, | ||
| 1113 | __in const BURN_PACKAGE* pPackage, | ||
| 1114 | __in_z LPCWSTR wzDependentProviderKey | ||
| 1115 | ) | ||
| 1116 | { | ||
| 1117 | HRESULT hr = S_OK; | ||
| 1118 | HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 1119 | |||
| 1120 | // Do not register a dependency on a package in a different install context. | ||
| 1121 | if (fPerMachine != pPackage->fPerMachine) | ||
| 1122 | { | ||
| 1123 | LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); | ||
| 1124 | ExitFunction1(hr = S_OK); | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | if (pPackage->rgDependencyProviders) | ||
| 1128 | { | ||
| 1129 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 1130 | { | ||
| 1131 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 1132 | |||
| 1133 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); | ||
| 1134 | |||
| 1135 | hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0); | ||
| 1136 | if (E_FILENOTFOUND != hr || pPackage->fVital) | ||
| 1137 | { | ||
| 1138 | ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey); | ||
| 1139 | } | ||
| 1140 | else | ||
| 1141 | { | ||
| 1142 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId); | ||
| 1143 | hr = S_OK; | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | LExit: | ||
| 1149 | return hr; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | /******************************************************************** | ||
| 1153 | UnregisterPackageDependency - Unregisters the provider key | ||
| 1154 | as a dependent of a package. | ||
| 1155 | |||
| 1156 | *********************************************************************/ | ||
| 1157 | static void UnregisterPackageDependency( | ||
| 1158 | __in BOOL fPerMachine, | ||
| 1159 | __in const BURN_PACKAGE* pPackage, | ||
| 1160 | __in_z LPCWSTR wzDependentProviderKey | ||
| 1161 | ) | ||
| 1162 | { | ||
| 1163 | HRESULT hr = S_OK; | ||
| 1164 | HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 1165 | |||
| 1166 | // Should be no registration to remove since we don't write keys across contexts. | ||
| 1167 | if (fPerMachine != pPackage->fPerMachine) | ||
| 1168 | { | ||
| 1169 | LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); | ||
| 1170 | return; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | // Loop through each package provider and remove the bundle dependency key. | ||
| 1174 | if (pPackage->rgDependencyProviders) | ||
| 1175 | { | ||
| 1176 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 1177 | { | ||
| 1178 | const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; | ||
| 1179 | |||
| 1180 | hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey); | ||
| 1181 | if (SUCCEEDED(hr)) | ||
| 1182 | { | ||
| 1183 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId); | ||
| 1184 | } | ||
| 1185 | else if (FAILED(hr) && E_FILENOTFOUND != hr) | ||
| 1186 | { | ||
| 1187 | LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr); | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | } | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | /******************************************************************** | ||
| 1194 | PackageProviderExists - Checks if a package provider is registered. | ||
| 1195 | |||
| 1196 | *********************************************************************/ | ||
| 1197 | static BOOL PackageProviderExists( | ||
| 1198 | __in const BURN_PACKAGE* pPackage | ||
| 1199 | ) | ||
| 1200 | { | ||
| 1201 | HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL); | ||
| 1202 | return SUCCEEDED(hr); | ||
| 1203 | } | ||
diff --git a/src/engine/dependency.h b/src/engine/dependency.h new file mode 100644 index 00000000..905857e0 --- /dev/null +++ b/src/engine/dependency.h | |||
| @@ -0,0 +1,176 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | // constants | ||
| 10 | |||
| 11 | const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES"; | ||
| 12 | |||
| 13 | |||
| 14 | // function declarations | ||
| 15 | |||
| 16 | /******************************************************************** | ||
| 17 | DependencyUninitialize - Frees and zeros memory allocated in the | ||
| 18 | dependency. | ||
| 19 | |||
| 20 | *********************************************************************/ | ||
| 21 | void DependencyUninitialize( | ||
| 22 | __in BURN_DEPENDENCY_PROVIDER* pProvider | ||
| 23 | ); | ||
| 24 | |||
| 25 | /******************************************************************** | ||
| 26 | DependencyParseProvidersFromXml - Parses dependency information | ||
| 27 | from the manifest for the specified package. | ||
| 28 | |||
| 29 | *********************************************************************/ | ||
| 30 | HRESULT DependencyParseProvidersFromXml( | ||
| 31 | __in BURN_PACKAGE* pPackage, | ||
| 32 | __in IXMLDOMNode* pixnPackage | ||
| 33 | ); | ||
| 34 | |||
| 35 | /******************************************************************** | ||
| 36 | DependencyDetectProviderKeyPackageId - Detect if the provider key is | ||
| 37 | registered and if so what package code is registered. | ||
| 38 | |||
| 39 | Note: Returns E_NOTFOUND if the provider key is not registered. | ||
| 40 | *********************************************************************/ | ||
| 41 | HRESULT DependencyDetectProviderKeyPackageId( | ||
| 42 | __in const BURN_PACKAGE* pPackage, | ||
| 43 | __deref_opt_out_z_opt LPWSTR* psczProviderKey, | ||
| 44 | __deref_opt_out_z_opt LPWSTR* psczId | ||
| 45 | ); | ||
| 46 | |||
| 47 | /******************************************************************** | ||
| 48 | DependencyDetectProviderKeyBundleId - Detect if the provider key is | ||
| 49 | registered and if so what bundle is registered. | ||
| 50 | |||
| 51 | Note: Returns E_NOTFOUND if the provider key is not registered. | ||
| 52 | *********************************************************************/ | ||
| 53 | HRESULT DependencyDetectProviderKeyBundleId( | ||
| 54 | __in BURN_REGISTRATION* pRegistration | ||
| 55 | ); | ||
| 56 | |||
| 57 | /******************************************************************** | ||
| 58 | DependencyPlanInitialize - Initializes the plan. | ||
| 59 | |||
| 60 | *********************************************************************/ | ||
| 61 | HRESULT DependencyPlanInitialize( | ||
| 62 | __in const BURN_ENGINE_STATE* pEngineState, | ||
| 63 | __in BURN_PLAN* pPlan | ||
| 64 | ); | ||
| 65 | |||
| 66 | /******************************************************************** | ||
| 67 | DependencyAllocIgnoreDependencies - Allocates the dependencies to | ||
| 68 | ignore as a semicolon-delimited string. | ||
| 69 | |||
| 70 | *********************************************************************/ | ||
| 71 | HRESULT DependencyAllocIgnoreDependencies( | ||
| 72 | __in const BURN_PLAN *pPlan, | ||
| 73 | __out_z LPWSTR* psczIgnoreDependencies | ||
| 74 | ); | ||
| 75 | |||
| 76 | /******************************************************************** | ||
| 77 | DependencyAddIgnoreDependencies - Populates the ignore dependency | ||
| 78 | names. | ||
| 79 | |||
| 80 | *********************************************************************/ | ||
| 81 | HRESULT DependencyAddIgnoreDependencies( | ||
| 82 | __in STRINGDICT_HANDLE sdIgnoreDependencies, | ||
| 83 | __in_z LPCWSTR wzAddIgnoreDependencies | ||
| 84 | ); | ||
| 85 | |||
| 86 | /******************************************************************** | ||
| 87 | DependencyDependentExists - Checks to see if the provider key is | ||
| 88 | already dependent on this bundle. | ||
| 89 | |||
| 90 | *********************************************************************/ | ||
| 91 | BOOL DependencyDependentExists( | ||
| 92 | __in const BURN_REGISTRATION* pRegistration, | ||
| 93 | __in_z LPCWSTR wzDependentProviderKey | ||
| 94 | ); | ||
| 95 | |||
| 96 | /******************************************************************** | ||
| 97 | DependencyPlanPackageBegin - Updates the dependency registration | ||
| 98 | action depending on the calculated state for the package. | ||
| 99 | |||
| 100 | *********************************************************************/ | ||
| 101 | HRESULT DependencyPlanPackageBegin( | ||
| 102 | __in BOOL fPerMachine, | ||
| 103 | __in BURN_PACKAGE* pPackage, | ||
| 104 | __in BURN_PLAN* pPlan | ||
| 105 | ); | ||
| 106 | |||
| 107 | /******************************************************************** | ||
| 108 | DependencyPlanPackage - adds dependency related actions to the plan | ||
| 109 | for this package. | ||
| 110 | |||
| 111 | *********************************************************************/ | ||
| 112 | HRESULT DependencyPlanPackage( | ||
| 113 | __in_opt DWORD *pdwInsertSequence, | ||
| 114 | __in const BURN_PACKAGE* pPackage, | ||
| 115 | __in BURN_PLAN* pPlan | ||
| 116 | ); | ||
| 117 | |||
| 118 | /******************************************************************** | ||
| 119 | DependencyPlanPackageComplete - Updates the dependency registration | ||
| 120 | action depending on the planned action for the package. | ||
| 121 | |||
| 122 | *********************************************************************/ | ||
| 123 | HRESULT DependencyPlanPackageComplete( | ||
| 124 | __in BURN_PACKAGE* pPackage, | ||
| 125 | __in BURN_PLAN* pPlan | ||
| 126 | ); | ||
| 127 | |||
| 128 | /******************************************************************** | ||
| 129 | DependencyExecutePackageProviderAction - Registers or unregisters | ||
| 130 | provider information for the package contained within the action. | ||
| 131 | |||
| 132 | *********************************************************************/ | ||
| 133 | HRESULT DependencyExecutePackageProviderAction( | ||
| 134 | __in const BURN_EXECUTE_ACTION* pAction | ||
| 135 | ); | ||
| 136 | |||
| 137 | /******************************************************************** | ||
| 138 | DependencyExecutePackageDependencyAction - Registers or unregisters | ||
| 139 | dependency information for the package contained within the action. | ||
| 140 | |||
| 141 | *********************************************************************/ | ||
| 142 | HRESULT DependencyExecutePackageDependencyAction( | ||
| 143 | __in BOOL fPerMachine, | ||
| 144 | __in const BURN_EXECUTE_ACTION* pAction | ||
| 145 | ); | ||
| 146 | |||
| 147 | /******************************************************************** | ||
| 148 | DependencyRegisterBundle - Registers the bundle dependency provider. | ||
| 149 | |||
| 150 | *********************************************************************/ | ||
| 151 | HRESULT DependencyRegisterBundle( | ||
| 152 | __in const BURN_REGISTRATION* pRegistration | ||
| 153 | ); | ||
| 154 | |||
| 155 | /******************************************************************** | ||
| 156 | DependencyProcessDependentRegistration - Registers or unregisters dependents | ||
| 157 | on the bundle based on the action. | ||
| 158 | |||
| 159 | *********************************************************************/ | ||
| 160 | HRESULT DependencyProcessDependentRegistration( | ||
| 161 | __in const BURN_REGISTRATION* pRegistration, | ||
| 162 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 163 | ); | ||
| 164 | |||
| 165 | /******************************************************************** | ||
| 166 | DependencyUnregisterBundle - Removes the bundle dependency provider. | ||
| 167 | |||
| 168 | Note: Does not check for existing dependents before removing the key. | ||
| 169 | *********************************************************************/ | ||
| 170 | void DependencyUnregisterBundle( | ||
| 171 | __in const BURN_REGISTRATION* pRegistration | ||
| 172 | ); | ||
| 173 | |||
| 174 | #if defined(__cplusplus) | ||
| 175 | } | ||
| 176 | #endif | ||
diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp new file mode 100644 index 00000000..7953daf5 --- /dev/null +++ b/src/engine/detect.cpp | |||
| @@ -0,0 +1,431 @@ | |||
| 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 | |||
| 5 | typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA | ||
| 6 | { | ||
| 7 | BURN_USER_EXPERIENCE* pUX; | ||
| 8 | LPCWSTR wzPackageOrContainerId; | ||
| 9 | } DETECT_AUTHENTICATION_REQUIRED_DATA; | ||
| 10 | |||
| 11 | // internal function definitions | ||
| 12 | static HRESULT AuthenticationRequired( | ||
| 13 | __in LPVOID pData, | ||
| 14 | __in HINTERNET hUrl, | ||
| 15 | __in long lHttpCode, | ||
| 16 | __out BOOL* pfRetrySend, | ||
| 17 | __out BOOL* pfRetry | ||
| 18 | ); | ||
| 19 | |||
| 20 | static HRESULT DetectAtomFeedUpdate( | ||
| 21 | __in_z LPCWSTR wzBundleId, | ||
| 22 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 23 | __in BURN_UPDATE* pUpdate | ||
| 24 | ); | ||
| 25 | |||
| 26 | static HRESULT DownloadUpdateFeed( | ||
| 27 | __in_z LPCWSTR wzBundleId, | ||
| 28 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 29 | __in BURN_UPDATE* pUpdate, | ||
| 30 | __deref_inout_z LPWSTR* psczTempFile | ||
| 31 | ); | ||
| 32 | |||
| 33 | // function definitions | ||
| 34 | |||
| 35 | extern "C" void DetectReset( | ||
| 36 | __in BURN_REGISTRATION* pRegistration, | ||
| 37 | __in BURN_PACKAGES* pPackages | ||
| 38 | ) | ||
| 39 | { | ||
| 40 | RelatedBundlesUninitialize(&pRegistration->relatedBundles); | ||
| 41 | ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); | ||
| 42 | pRegistration->fEnabledForwardCompatibleBundle = FALSE; | ||
| 43 | PackageUninitialize(&pRegistration->forwardCompatibleBundle); | ||
| 44 | |||
| 45 | for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) | ||
| 46 | { | ||
| 47 | BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; | ||
| 48 | |||
| 49 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; | ||
| 50 | |||
| 51 | pPackage->cache = BURN_CACHE_STATE_NONE; | ||
| 52 | for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) | ||
| 53 | { | ||
| 54 | BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; | ||
| 55 | pPayload->fCached = FALSE; | ||
| 56 | } | ||
| 57 | |||
| 58 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type) | ||
| 59 | { | ||
| 60 | for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) | ||
| 61 | { | ||
| 62 | BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; | ||
| 63 | |||
| 64 | pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; | ||
| 65 | } | ||
| 66 | |||
| 67 | pPackage->Msi.fCompatibleInstalled = FALSE; | ||
| 68 | } | ||
| 69 | else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 70 | { | ||
| 71 | ReleaseNullMem(pPackage->Msp.rgTargetProducts); | ||
| 72 | pPackage->Msp.cTargetProductCodes = 0; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) | ||
| 77 | { | ||
| 78 | MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; | ||
| 79 | pPatchInfo->dwOrder = 0; | ||
| 80 | pPatchInfo->uStatus = 0; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | extern "C" HRESULT DetectForwardCompatibleBundle( | ||
| 85 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 86 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 87 | __in BURN_REGISTRATION* pRegistration | ||
| 88 | ) | ||
| 89 | { | ||
| 90 | HRESULT hr = S_OK; | ||
| 91 | BOOL fRecommendIgnore = TRUE; | ||
| 92 | BOOL fIgnoreBundle = FALSE; | ||
| 93 | |||
| 94 | if (pRegistration->sczDetectedProviderKeyBundleId && | ||
| 95 | CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) | ||
| 96 | { | ||
| 97 | // Only change the recommendation if an active parent was provided. | ||
| 98 | if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) | ||
| 99 | { | ||
| 100 | // On install, recommend running the forward compatible bundle because there is an active parent. This | ||
| 101 | // will essentially register the parent with the forward compatible bundle. | ||
| 102 | if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action) | ||
| 103 | { | ||
| 104 | fRecommendIgnore = FALSE; | ||
| 105 | } | ||
| 106 | else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action || | ||
| 107 | BOOTSTRAPPER_ACTION_MODIFY == pCommand->action || | ||
| 108 | BOOTSTRAPPER_ACTION_REPAIR == pCommand->action) | ||
| 109 | { | ||
| 110 | // When modifying the bundle, only recommend running the forward compatible bundle if the parent | ||
| 111 | // is already registered as a dependent of the provider key. | ||
| 112 | if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent)) | ||
| 113 | { | ||
| 114 | fRecommendIgnore = FALSE; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) | ||
| 120 | { | ||
| 121 | BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; | ||
| 122 | fIgnoreBundle = fRecommendIgnore; | ||
| 123 | |||
| 124 | if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && | ||
| 125 | pRegistration->qwVersion <= pRelatedBundle->qwVersion && | ||
| 126 | CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) | ||
| 127 | { | ||
| 128 | hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle); | ||
| 129 | ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); | ||
| 130 | |||
| 131 | if (!fIgnoreBundle) | ||
| 132 | { | ||
| 133 | hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); | ||
| 134 | ExitOnFailure(hr, "Failed to initialize update bundle."); | ||
| 135 | |||
| 136 | pRegistration->fEnabledForwardCompatibleBundle = TRUE; | ||
| 137 | } | ||
| 138 | |||
| 139 | LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | LExit: | ||
| 146 | return hr; | ||
| 147 | } | ||
| 148 | |||
| 149 | extern "C" HRESULT DetectReportRelatedBundles( | ||
| 150 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 151 | __in BURN_REGISTRATION* pRegistration, | ||
| 152 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 153 | __in BOOTSTRAPPER_ACTION action | ||
| 154 | ) | ||
| 155 | { | ||
| 156 | HRESULT hr = S_OK; | ||
| 157 | |||
| 158 | for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) | ||
| 159 | { | ||
| 160 | const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; | ||
| 161 | BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; | ||
| 162 | |||
| 163 | switch (pRelatedBundle->relationType) | ||
| 164 | { | ||
| 165 | case BOOTSTRAPPER_RELATION_UPGRADE: | ||
| 166 | if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) | ||
| 167 | { | ||
| 168 | if (pRegistration->qwVersion > pRelatedBundle->qwVersion) | ||
| 169 | { | ||
| 170 | operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; | ||
| 171 | } | ||
| 172 | else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) | ||
| 173 | { | ||
| 174 | operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | break; | ||
| 178 | |||
| 179 | case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; | ||
| 180 | case BOOTSTRAPPER_RELATION_ADDON: | ||
| 181 | if (BOOTSTRAPPER_ACTION_UNINSTALL == action) | ||
| 182 | { | ||
| 183 | operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; | ||
| 184 | } | ||
| 185 | else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) | ||
| 186 | { | ||
| 187 | operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; | ||
| 188 | } | ||
| 189 | else if (BOOTSTRAPPER_ACTION_REPAIR == action) | ||
| 190 | { | ||
| 191 | operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; | ||
| 192 | } | ||
| 193 | break; | ||
| 194 | |||
| 195 | case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; | ||
| 196 | case BOOTSTRAPPER_RELATION_DEPENDENT: | ||
| 197 | break; | ||
| 198 | |||
| 199 | default: | ||
| 200 | hr = E_FAIL; | ||
| 201 | ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | |||
| 205 | LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); | ||
| 206 | |||
| 207 | hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); | ||
| 208 | ExitOnRootFailure(hr, "BA aborted detect related bundle."); | ||
| 209 | } | ||
| 210 | |||
| 211 | LExit: | ||
| 212 | return hr; | ||
| 213 | } | ||
| 214 | |||
| 215 | extern "C" HRESULT DetectUpdate( | ||
| 216 | __in_z LPCWSTR wzBundleId, | ||
| 217 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 218 | __in BURN_UPDATE* pUpdate | ||
| 219 | ) | ||
| 220 | { | ||
| 221 | HRESULT hr = S_OK; | ||
| 222 | BOOL fBeginCalled = FALSE; | ||
| 223 | BOOL fSkip = TRUE; | ||
| 224 | BOOL fIgnoreError = FALSE; | ||
| 225 | |||
| 226 | // If no update source was specified, skip update detection. | ||
| 227 | if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) | ||
| 228 | { | ||
| 229 | ExitFunction(); | ||
| 230 | } | ||
| 231 | |||
| 232 | fBeginCalled = TRUE; | ||
| 233 | hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip); | ||
| 234 | ExitOnRootFailure(hr, "BA aborted detect update begin."); | ||
| 235 | |||
| 236 | if (!fSkip) | ||
| 237 | { | ||
| 238 | hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate); | ||
| 239 | ExitOnFailure(hr, "Failed to detect atom feed update."); | ||
| 240 | } | ||
| 241 | |||
| 242 | LExit: | ||
| 243 | if (fBeginCalled) | ||
| 244 | { | ||
| 245 | UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError); | ||
| 246 | if (fIgnoreError) | ||
| 247 | { | ||
| 248 | hr = S_OK; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | return hr; | ||
| 253 | } | ||
| 254 | |||
| 255 | static HRESULT AuthenticationRequired( | ||
| 256 | __in LPVOID pData, | ||
| 257 | __in HINTERNET hUrl, | ||
| 258 | __in long lHttpCode, | ||
| 259 | __out BOOL* pfRetrySend, | ||
| 260 | __out BOOL* pfRetry | ||
| 261 | ) | ||
| 262 | { | ||
| 263 | Assert(401 == lHttpCode || 407 == lHttpCode); | ||
| 264 | |||
| 265 | HRESULT hr = S_OK; | ||
| 266 | DWORD er = ERROR_SUCCESS; | ||
| 267 | BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY; | ||
| 268 | LPWSTR sczError = NULL; | ||
| 269 | DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData); | ||
| 270 | int nResult = IDNOACTION; | ||
| 271 | |||
| 272 | *pfRetrySend = FALSE; | ||
| 273 | *pfRetry = FALSE; | ||
| 274 | |||
| 275 | hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL); | ||
| 276 | ExitOnFailure(hr, "Failed to allocation error string."); | ||
| 277 | |||
| 278 | UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value. | ||
| 279 | nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult); | ||
| 280 | if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect) | ||
| 281 | { | ||
| 282 | er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); | ||
| 283 | if (ERROR_SUCCESS == er || ERROR_CANCELLED == er) | ||
| 284 | { | ||
| 285 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 286 | } | ||
| 287 | else if (ERROR_INTERNET_FORCE_RETRY == er) | ||
| 288 | { | ||
| 289 | *pfRetrySend = TRUE; | ||
| 290 | hr = S_OK; | ||
| 291 | } | ||
| 292 | else | ||
| 293 | { | ||
| 294 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | else if (IDRETRY == nResult) | ||
| 298 | { | ||
| 299 | *pfRetry = TRUE; | ||
| 300 | hr = S_OK; | ||
| 301 | } | ||
| 302 | else | ||
| 303 | { | ||
| 304 | hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | ||
| 305 | } | ||
| 306 | |||
| 307 | LExit: | ||
| 308 | ReleaseStr(sczError); | ||
| 309 | |||
| 310 | return hr; | ||
| 311 | } | ||
| 312 | |||
| 313 | static HRESULT DownloadUpdateFeed( | ||
| 314 | __in_z LPCWSTR wzBundleId, | ||
| 315 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 316 | __in BURN_UPDATE* pUpdate, | ||
| 317 | __deref_inout_z LPWSTR* psczTempFile | ||
| 318 | ) | ||
| 319 | { | ||
| 320 | HRESULT hr = S_OK; | ||
| 321 | DOWNLOAD_SOURCE downloadSource = { }; | ||
| 322 | DOWNLOAD_CACHE_CALLBACK cacheCallback = { }; | ||
| 323 | DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { }; | ||
| 324 | DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { }; | ||
| 325 | LPWSTR sczUpdateId = NULL; | ||
| 326 | LPWSTR sczError = NULL; | ||
| 327 | DWORD64 qwDownloadSize = 0; | ||
| 328 | |||
| 329 | // Always do our work in the working folder, even if cached. | ||
| 330 | hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL); | ||
| 331 | ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time."); | ||
| 332 | |||
| 333 | // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here | ||
| 334 | hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0); | ||
| 335 | ExitOnFailure(hr, "Failed to copy update url."); | ||
| 336 | |||
| 337 | cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine; | ||
| 338 | cacheCallback.pfnCancel = NULL; // TODO: set this | ||
| 339 | cacheCallback.pv = NULL; //pProgress; | ||
| 340 | |||
| 341 | authenticationData.pUX = pUX; | ||
| 342 | authenticationData.wzPackageOrContainerId = wzBundleId; | ||
| 343 | |||
| 344 | authenticationCallback.pv = static_cast<LPVOID>(&authenticationData); | ||
| 345 | authenticationCallback.pfnAuthenticate = &AuthenticationRequired; | ||
| 346 | |||
| 347 | hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback); | ||
| 348 | ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile); | ||
| 349 | |||
| 350 | LExit: | ||
| 351 | if (FAILED(hr)) | ||
| 352 | { | ||
| 353 | if (*psczTempFile) | ||
| 354 | { | ||
| 355 | FileEnsureDelete(*psczTempFile); | ||
| 356 | } | ||
| 357 | |||
| 358 | ReleaseNullStr(*psczTempFile); | ||
| 359 | } | ||
| 360 | |||
| 361 | ReleaseStr(downloadSource.sczUrl); | ||
| 362 | ReleaseStr(downloadSource.sczUser); | ||
| 363 | ReleaseStr(downloadSource.sczPassword); | ||
| 364 | ReleaseStr(sczUpdateId); | ||
| 365 | ReleaseStr(sczError); | ||
| 366 | return hr; | ||
| 367 | } | ||
| 368 | |||
| 369 | |||
| 370 | static HRESULT DetectAtomFeedUpdate( | ||
| 371 | __in_z LPCWSTR wzBundleId, | ||
| 372 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 373 | __in BURN_UPDATE* pUpdate | ||
| 374 | ) | ||
| 375 | { | ||
| 376 | Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource); | ||
| 377 | #ifdef DEBUG | ||
| 378 | LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource); | ||
| 379 | #endif | ||
| 380 | |||
| 381 | |||
| 382 | HRESULT hr = S_OK; | ||
| 383 | LPWSTR sczUpdateFeedTempFile = NULL; | ||
| 384 | ATOM_FEED* pAtomFeed = NULL; | ||
| 385 | APPLICATION_UPDATE_CHAIN* pApupChain = NULL; | ||
| 386 | BOOL fStopProcessingUpdates = FALSE; | ||
| 387 | |||
| 388 | hr = AtomInitialize(); | ||
| 389 | ExitOnFailure(hr, "Failed to initialize Atom."); | ||
| 390 | |||
| 391 | hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile); | ||
| 392 | ExitOnFailure(hr, "Failed to download update feed."); | ||
| 393 | |||
| 394 | hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed); | ||
| 395 | ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile); | ||
| 396 | |||
| 397 | hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain); | ||
| 398 | ExitOnFailure(hr, "Failed to allocate update chain from atom feed."); | ||
| 399 | |||
| 400 | if (0 < pApupChain->cEntries) | ||
| 401 | { | ||
| 402 | for (DWORD i = 0; i < pApupChain->cEntries; ++i) | ||
| 403 | { | ||
| 404 | APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i]; | ||
| 405 | |||
| 406 | hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL, | ||
| 407 | pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0, | ||
| 408 | pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle, | ||
| 409 | pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates); | ||
| 410 | ExitOnRootFailure(hr, "BA aborted detect update."); | ||
| 411 | |||
| 412 | if (fStopProcessingUpdates) | ||
| 413 | { | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | LExit: | ||
| 420 | if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile) | ||
| 421 | { | ||
| 422 | FileEnsureDelete(sczUpdateFeedTempFile); | ||
| 423 | } | ||
| 424 | |||
| 425 | ApupFreeChain(pApupChain); | ||
| 426 | AtomFreeFeed(pAtomFeed); | ||
| 427 | ReleaseStr(sczUpdateFeedTempFile); | ||
| 428 | AtomUninitialize(); | ||
| 429 | |||
| 430 | return hr; | ||
| 431 | } | ||
diff --git a/src/engine/detect.h b/src/engine/detect.h new file mode 100644 index 00000000..01488f1a --- /dev/null +++ b/src/engine/detect.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | |||
| 13 | // structs | ||
| 14 | |||
| 15 | |||
| 16 | // functions | ||
| 17 | |||
| 18 | void DetectReset( | ||
| 19 | __in BURN_REGISTRATION* pRegistration, | ||
| 20 | __in BURN_PACKAGES* pPackages | ||
| 21 | ); | ||
| 22 | |||
| 23 | HRESULT DetectForwardCompatibleBundle( | ||
| 24 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 25 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 26 | __in BURN_REGISTRATION* pRegistration | ||
| 27 | ); | ||
| 28 | |||
| 29 | HRESULT DetectReportRelatedBundles( | ||
| 30 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 31 | __in BURN_REGISTRATION* pRegistration, | ||
| 32 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 33 | __in BOOTSTRAPPER_ACTION action | ||
| 34 | ); | ||
| 35 | |||
| 36 | HRESULT DetectUpdate( | ||
| 37 | __in_z LPCWSTR wzBundleId, | ||
| 38 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 39 | __in BURN_UPDATE* pUpdate | ||
| 40 | ); | ||
| 41 | |||
| 42 | #if defined(__cplusplus) | ||
| 43 | } | ||
| 44 | #endif | ||
diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp new file mode 100644 index 00000000..1b9336e0 --- /dev/null +++ b/src/engine/elevation.cpp | |||
| @@ -0,0 +1,2814 @@ | |||
| 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 | |||
| 5 | |||
| 6 | const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? | ||
| 7 | |||
| 8 | typedef enum _BURN_ELEVATION_MESSAGE_TYPE | ||
| 9 | { | ||
| 10 | BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN, | ||
| 11 | BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, | ||
| 12 | BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, | ||
| 13 | BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, | ||
| 14 | BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, | ||
| 15 | BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, | ||
| 16 | BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, | ||
| 17 | BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, | ||
| 18 | BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, | ||
| 19 | BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, | ||
| 20 | BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, | ||
| 21 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, | ||
| 22 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, | ||
| 23 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, | ||
| 24 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, | ||
| 25 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, | ||
| 26 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, | ||
| 27 | BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, | ||
| 28 | BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD, | ||
| 29 | BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, | ||
| 30 | BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, | ||
| 31 | |||
| 32 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS, | ||
| 33 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR, | ||
| 34 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE, | ||
| 35 | BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE, | ||
| 36 | BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, | ||
| 37 | |||
| 38 | BURN_ELEVATION_TRANSACTION_BEGIN, | ||
| 39 | BURN_ELEVATION_TRANSACTION_COMMIT, | ||
| 40 | BURN_ELEVATION_TRANSACTION_ROLLBACK | ||
| 41 | |||
| 42 | } BURN_ELEVATION_MESSAGE_TYPE; | ||
| 43 | |||
| 44 | |||
| 45 | // struct | ||
| 46 | |||
| 47 | typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT | ||
| 48 | { | ||
| 49 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
| 50 | LPVOID pvContext; | ||
| 51 | } BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; | ||
| 52 | |||
| 53 | typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT | ||
| 54 | { | ||
| 55 | PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; | ||
| 56 | LPVOID pvContext; | ||
| 57 | } BURN_ELEVATION_MSI_MESSAGE_CONTEXT; | ||
| 58 | |||
| 59 | typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT | ||
| 60 | { | ||
| 61 | DWORD dwProcessId; | ||
| 62 | } BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; | ||
| 63 | |||
| 64 | typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT | ||
| 65 | { | ||
| 66 | DWORD dwLoggingTlsId; | ||
| 67 | HANDLE hPipe; | ||
| 68 | HANDLE* phLock; | ||
| 69 | BOOL* pfDisabledAutomaticUpdates; | ||
| 70 | BURN_APPROVED_EXES* pApprovedExes; | ||
| 71 | BURN_CONTAINERS* pContainers; | ||
| 72 | BURN_PACKAGES* pPackages; | ||
| 73 | BURN_PAYLOADS* pPayloads; | ||
| 74 | BURN_VARIABLES* pVariables; | ||
| 75 | BURN_REGISTRATION* pRegistration; | ||
| 76 | BURN_USER_EXPERIENCE* pUserExperience; | ||
| 77 | |||
| 78 | MSIHANDLE hMsiTrns; | ||
| 79 | HANDLE hMsiTrnsEvent; | ||
| 80 | } BURN_ELEVATION_CHILD_MESSAGE_CONTEXT; | ||
| 81 | |||
| 82 | |||
| 83 | // internal function declarations | ||
| 84 | |||
| 85 | static HRESULT OnMsiBeginTransaction( | ||
| 86 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 87 | ); | ||
| 88 | static HRESULT OnMsiCommitTransaction( | ||
| 89 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 90 | ); | ||
| 91 | static HRESULT OnMsiRollbackTransaction( | ||
| 92 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 93 | ); | ||
| 94 | |||
| 95 | static DWORD WINAPI ElevatedChildCacheThreadProc( | ||
| 96 | __in LPVOID lpThreadParameter | ||
| 97 | ); | ||
| 98 | static HRESULT WaitForElevatedChildCacheThread( | ||
| 99 | __in HANDLE hCacheThread, | ||
| 100 | __in DWORD dwExpectedExitCode | ||
| 101 | ); | ||
| 102 | static HRESULT OnLoadCompatiblePackage( | ||
| 103 | __in BURN_PACKAGES* pPackages, | ||
| 104 | __in BYTE* pbData, | ||
| 105 | __in DWORD cbData | ||
| 106 | ); | ||
| 107 | static HRESULT ProcessGenericExecuteMessages( | ||
| 108 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 109 | __in_opt LPVOID pvContext, | ||
| 110 | __out DWORD* pdwResult | ||
| 111 | ); | ||
| 112 | static HRESULT ProcessMsiPackageMessages( | ||
| 113 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 114 | __in_opt LPVOID pvContext, | ||
| 115 | __out DWORD* pdwResult | ||
| 116 | ); | ||
| 117 | static HRESULT ProcessLaunchApprovedExeMessages( | ||
| 118 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 119 | __in_opt LPVOID pvContext, | ||
| 120 | __out DWORD* pdwResult | ||
| 121 | ); | ||
| 122 | static HRESULT ProcessElevatedChildMessage( | ||
| 123 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 124 | __in_opt LPVOID pvContext, | ||
| 125 | __out DWORD* pdwResult | ||
| 126 | ); | ||
| 127 | static HRESULT ProcessElevatedChildCacheMessage( | ||
| 128 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 129 | __in_opt LPVOID pvContext, | ||
| 130 | __out DWORD* pdwResult | ||
| 131 | ); | ||
| 132 | static HRESULT ProcessResult( | ||
| 133 | __in DWORD dwResult, | ||
| 134 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 135 | ); | ||
| 136 | static HRESULT OnApplyInitialize( | ||
| 137 | __in BURN_VARIABLES* pVariables, | ||
| 138 | __in BURN_REGISTRATION* pRegistration, | ||
| 139 | __in HANDLE* phLock, | ||
| 140 | __in BOOL* pfDisabledWindowsUpdate, | ||
| 141 | __in BYTE* pbData, | ||
| 142 | __in DWORD cbData | ||
| 143 | ); | ||
| 144 | static HRESULT OnApplyUninitialize( | ||
| 145 | __in HANDLE* phLock | ||
| 146 | ); | ||
| 147 | static HRESULT OnSessionBegin( | ||
| 148 | __in BURN_REGISTRATION* pRegistration, | ||
| 149 | __in BURN_VARIABLES* pVariables, | ||
| 150 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 151 | __in BYTE* pbData, | ||
| 152 | __in DWORD cbData | ||
| 153 | ); | ||
| 154 | static HRESULT OnSessionResume( | ||
| 155 | __in BURN_REGISTRATION* pRegistration, | ||
| 156 | __in BURN_VARIABLES* pVariables, | ||
| 157 | __in BYTE* pbData, | ||
| 158 | __in DWORD cbData | ||
| 159 | ); | ||
| 160 | static HRESULT OnSessionEnd( | ||
| 161 | __in BURN_REGISTRATION* pRegistration, | ||
| 162 | __in BYTE* pbData, | ||
| 163 | __in DWORD cbData | ||
| 164 | ); | ||
| 165 | static HRESULT OnSaveState( | ||
| 166 | __in BURN_REGISTRATION* pRegistration, | ||
| 167 | __in BYTE* pbData, | ||
| 168 | __in DWORD cbData | ||
| 169 | ); | ||
| 170 | static HRESULT OnLayoutBundle( | ||
| 171 | __in_z LPCWSTR wzExecutableName, | ||
| 172 | __in BYTE* pbData, | ||
| 173 | __in DWORD cbData | ||
| 174 | ); | ||
| 175 | static HRESULT OnCacheOrLayoutContainerOrPayload( | ||
| 176 | __in BURN_CONTAINERS* pContainers, | ||
| 177 | __in BURN_PACKAGES* pPackages, | ||
| 178 | __in BURN_PAYLOADS* pPayloads, | ||
| 179 | __in BYTE* pbData, | ||
| 180 | __in DWORD cbData | ||
| 181 | ); | ||
| 182 | static void OnCacheCleanup( | ||
| 183 | __in_z LPCWSTR wzBundleId | ||
| 184 | ); | ||
| 185 | static HRESULT OnProcessDependentRegistration( | ||
| 186 | __in const BURN_REGISTRATION* pRegistration, | ||
| 187 | __in BYTE* pbData, | ||
| 188 | __in DWORD cbData | ||
| 189 | ); | ||
| 190 | static HRESULT OnExecuteExePackage( | ||
| 191 | __in HANDLE hPipe, | ||
| 192 | __in BURN_PACKAGES* pPackages, | ||
| 193 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 194 | __in BURN_VARIABLES* pVariables, | ||
| 195 | __in BYTE* pbData, | ||
| 196 | __in DWORD cbData | ||
| 197 | ); | ||
| 198 | static HRESULT OnExecuteMsiPackage( | ||
| 199 | __in HANDLE hPipe, | ||
| 200 | __in BURN_PACKAGES* pPackages, | ||
| 201 | __in BURN_VARIABLES* pVariables, | ||
| 202 | __in BYTE* pbData, | ||
| 203 | __in DWORD cbData | ||
| 204 | ); | ||
| 205 | static HRESULT OnExecuteMspPackage( | ||
| 206 | __in HANDLE hPipe, | ||
| 207 | __in BURN_PACKAGES* pPackages, | ||
| 208 | __in BURN_VARIABLES* pVariables, | ||
| 209 | __in BYTE* pbData, | ||
| 210 | __in DWORD cbData | ||
| 211 | ); | ||
| 212 | static HRESULT OnExecuteMsuPackage( | ||
| 213 | __in HANDLE hPipe, | ||
| 214 | __in BURN_PACKAGES* pPackages, | ||
| 215 | __in BURN_VARIABLES* pVariables, | ||
| 216 | __in BYTE* pbData, | ||
| 217 | __in DWORD cbData | ||
| 218 | ); | ||
| 219 | static HRESULT OnExecutePackageProviderAction( | ||
| 220 | __in BURN_PACKAGES* pPackages, | ||
| 221 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 222 | __in BYTE* pbData, | ||
| 223 | __in DWORD cbData | ||
| 224 | ); | ||
| 225 | static HRESULT OnExecutePackageDependencyAction( | ||
| 226 | __in BURN_PACKAGES* pPackages, | ||
| 227 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 228 | __in BYTE* pbData, | ||
| 229 | __in DWORD cbData | ||
| 230 | ); | ||
| 231 | static int GenericExecuteMessageHandler( | ||
| 232 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
| 233 | __in LPVOID pvContext | ||
| 234 | ); | ||
| 235 | static int MsiExecuteMessageHandler( | ||
| 236 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
| 237 | __in_opt LPVOID pvContext | ||
| 238 | ); | ||
| 239 | static HRESULT OnCleanPackage( | ||
| 240 | __in BURN_PACKAGES* pPackages, | ||
| 241 | __in BYTE* pbData, | ||
| 242 | __in DWORD cbData | ||
| 243 | ); | ||
| 244 | static HRESULT OnLaunchApprovedExe( | ||
| 245 | __in HANDLE hPipe, | ||
| 246 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 247 | __in BURN_VARIABLES* pVariables, | ||
| 248 | __in BYTE* pbData, | ||
| 249 | __in DWORD cbData | ||
| 250 | ); | ||
| 251 | |||
| 252 | |||
| 253 | // function definitions | ||
| 254 | |||
| 255 | extern "C" HRESULT ElevationElevate( | ||
| 256 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 257 | __in_opt HWND hwndParent | ||
| 258 | ) | ||
| 259 | { | ||
| 260 | Assert(BURN_MODE_ELEVATED != pEngineState->mode); | ||
| 261 | Assert(!pEngineState->companionConnection.sczName); | ||
| 262 | Assert(!pEngineState->companionConnection.sczSecret); | ||
| 263 | Assert(!pEngineState->companionConnection.hProcess); | ||
| 264 | Assert(!pEngineState->companionConnection.dwProcessId); | ||
| 265 | Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe); | ||
| 266 | Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe); | ||
| 267 | |||
| 268 | HRESULT hr = S_OK; | ||
| 269 | int nResult = IDOK; | ||
| 270 | HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE; | ||
| 271 | |||
| 272 | hr = UserExperienceOnElevateBegin(&pEngineState->userExperience); | ||
| 273 | ExitOnRootFailure(hr, "BA aborted elevation requirement."); | ||
| 274 | |||
| 275 | hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret); | ||
| 276 | ExitOnFailure(hr, "Failed to create pipe name and client token."); | ||
| 277 | |||
| 278 | hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent); | ||
| 279 | ExitOnFailure(hr, "Failed to create pipe and cache pipe."); | ||
| 280 | |||
| 281 | LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING); | ||
| 282 | |||
| 283 | do | ||
| 284 | { | ||
| 285 | nResult = IDOK; | ||
| 286 | |||
| 287 | // Create the elevated process and if successful, wait for it to connect. | ||
| 288 | hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent); | ||
| 289 | if (SUCCEEDED(hr)) | ||
| 290 | { | ||
| 291 | LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS); | ||
| 292 | |||
| 293 | hr = PipeWaitForChildConnect(&pEngineState->companionConnection); | ||
| 294 | ExitOnFailure(hr, "Failed to connect to elevated child process."); | ||
| 295 | |||
| 296 | LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS); | ||
| 297 | } | ||
| 298 | else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) | ||
| 299 | { | ||
| 300 | // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. | ||
| 301 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 302 | nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); | ||
| 303 | } | ||
| 304 | } while (IDRETRY == nResult); | ||
| 305 | ExitOnFailure(hr, "Failed to elevate."); | ||
| 306 | |||
| 307 | LExit: | ||
| 308 | ReleaseHandle(hPipesCreatedEvent); | ||
| 309 | |||
| 310 | if (FAILED(hr)) | ||
| 311 | { | ||
| 312 | PipeConnectionUninitialize(&pEngineState->companionConnection); | ||
| 313 | } | ||
| 314 | |||
| 315 | UserExperienceOnElevateComplete(&pEngineState->userExperience, hr); | ||
| 316 | |||
| 317 | return hr; | ||
| 318 | } | ||
| 319 | |||
| 320 | extern "C" HRESULT ElevationApplyInitialize( | ||
| 321 | __in HANDLE hPipe, | ||
| 322 | __in BURN_VARIABLES* pVariables, | ||
| 323 | __in BOOTSTRAPPER_ACTION action, | ||
| 324 | __in BURN_AU_PAUSE_ACTION auAction, | ||
| 325 | __in BOOL fTakeSystemRestorePoint | ||
| 326 | ) | ||
| 327 | { | ||
| 328 | HRESULT hr = S_OK; | ||
| 329 | BYTE* pbData = NULL; | ||
| 330 | SIZE_T cbData = 0; | ||
| 331 | DWORD dwResult = 0; | ||
| 332 | |||
| 333 | // serialize message data | ||
| 334 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action); | ||
| 335 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 336 | |||
| 337 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction); | ||
| 338 | ExitOnFailure(hr, "Failed to write update action to message buffer."); | ||
| 339 | |||
| 340 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint); | ||
| 341 | ExitOnFailure(hr, "Failed to write system restore point action to message buffer."); | ||
| 342 | |||
| 343 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 344 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 345 | |||
| 346 | // send message | ||
| 347 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, NULL, NULL, &dwResult); | ||
| 348 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 349 | |||
| 350 | hr = (HRESULT)dwResult; | ||
| 351 | |||
| 352 | LExit: | ||
| 353 | ReleaseBuffer(pbData); | ||
| 354 | |||
| 355 | return hr; | ||
| 356 | } | ||
| 357 | |||
| 358 | extern "C" HRESULT ElevationApplyUninitialize( | ||
| 359 | __in HANDLE hPipe | ||
| 360 | ) | ||
| 361 | { | ||
| 362 | HRESULT hr = S_OK; | ||
| 363 | BYTE* pbData = NULL; | ||
| 364 | SIZE_T cbData = 0; | ||
| 365 | DWORD dwResult = 0; | ||
| 366 | |||
| 367 | // send message | ||
| 368 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult); | ||
| 369 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 370 | |||
| 371 | hr = (HRESULT)dwResult; | ||
| 372 | |||
| 373 | LExit: | ||
| 374 | ReleaseBuffer(pbData); | ||
| 375 | |||
| 376 | return hr; | ||
| 377 | } | ||
| 378 | |||
| 379 | /******************************************************************* | ||
| 380 | ElevationSessionBegin - | ||
| 381 | |||
| 382 | *******************************************************************/ | ||
| 383 | extern "C" HRESULT ElevationSessionBegin( | ||
| 384 | __in HANDLE hPipe, | ||
| 385 | __in_z LPCWSTR wzEngineWorkingPath, | ||
| 386 | __in_z LPCWSTR wzResumeCommandLine, | ||
| 387 | __in BOOL fDisableResume, | ||
| 388 | __in BURN_VARIABLES* pVariables, | ||
| 389 | __in DWORD dwRegistrationOperations, | ||
| 390 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, | ||
| 391 | __in DWORD64 qwEstimatedSize | ||
| 392 | ) | ||
| 393 | { | ||
| 394 | HRESULT hr = S_OK; | ||
| 395 | BYTE* pbData = NULL; | ||
| 396 | SIZE_T cbData = 0; | ||
| 397 | DWORD dwResult = 0; | ||
| 398 | |||
| 399 | // serialize message data | ||
| 400 | hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath); | ||
| 401 | ExitOnFailure(hr, "Failed to write engine working path to message buffer."); | ||
| 402 | |||
| 403 | hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); | ||
| 404 | ExitOnFailure(hr, "Failed to write resume command line to message buffer."); | ||
| 405 | |||
| 406 | hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); | ||
| 407 | ExitOnFailure(hr, "Failed to write resume flag."); | ||
| 408 | |||
| 409 | hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations); | ||
| 410 | ExitOnFailure(hr, "Failed to write registration operations to message buffer."); | ||
| 411 | |||
| 412 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); | ||
| 413 | ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); | ||
| 414 | |||
| 415 | hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize); | ||
| 416 | ExitOnFailure(hr, "Failed to write estimated size to message buffer."); | ||
| 417 | |||
| 418 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 419 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 420 | |||
| 421 | // send message | ||
| 422 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult); | ||
| 423 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 424 | |||
| 425 | hr = (HRESULT)dwResult; | ||
| 426 | |||
| 427 | LExit: | ||
| 428 | ReleaseBuffer(pbData); | ||
| 429 | |||
| 430 | return hr; | ||
| 431 | } | ||
| 432 | |||
| 433 | /******************************************************************* | ||
| 434 | ElevationSessionResume - | ||
| 435 | |||
| 436 | *******************************************************************/ | ||
| 437 | extern "C" HRESULT ElevationSessionResume( | ||
| 438 | __in HANDLE hPipe, | ||
| 439 | __in_z LPCWSTR wzResumeCommandLine, | ||
| 440 | __in BOOL fDisableResume, | ||
| 441 | __in BURN_VARIABLES* pVariables | ||
| 442 | ) | ||
| 443 | { | ||
| 444 | HRESULT hr = S_OK; | ||
| 445 | BYTE* pbData = NULL; | ||
| 446 | SIZE_T cbData = 0; | ||
| 447 | DWORD dwResult = 0; | ||
| 448 | |||
| 449 | // serialize message data | ||
| 450 | hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine); | ||
| 451 | ExitOnFailure(hr, "Failed to write resume command line to message buffer."); | ||
| 452 | |||
| 453 | hr = BuffWriteNumber(&pbData, &cbData, fDisableResume); | ||
| 454 | ExitOnFailure(hr, "Failed to write resume flag."); | ||
| 455 | |||
| 456 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 457 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 458 | |||
| 459 | // send message | ||
| 460 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult); | ||
| 461 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 462 | |||
| 463 | hr = (HRESULT)dwResult; | ||
| 464 | |||
| 465 | LExit: | ||
| 466 | ReleaseBuffer(pbData); | ||
| 467 | |||
| 468 | return hr; | ||
| 469 | } | ||
| 470 | |||
| 471 | /******************************************************************* | ||
| 472 | ElevationSessionEnd - | ||
| 473 | |||
| 474 | *******************************************************************/ | ||
| 475 | extern "C" HRESULT ElevationSessionEnd( | ||
| 476 | __in HANDLE hPipe, | ||
| 477 | __in BURN_RESUME_MODE resumeMode, | ||
| 478 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 479 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction | ||
| 480 | ) | ||
| 481 | { | ||
| 482 | HRESULT hr = S_OK; | ||
| 483 | BYTE* pbData = NULL; | ||
| 484 | SIZE_T cbData = 0; | ||
| 485 | DWORD dwResult = 0; | ||
| 486 | |||
| 487 | // serialize message data | ||
| 488 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode); | ||
| 489 | ExitOnFailure(hr, "Failed to write resume mode to message buffer."); | ||
| 490 | |||
| 491 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart); | ||
| 492 | ExitOnFailure(hr, "Failed to write restart enum to message buffer."); | ||
| 493 | |||
| 494 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction); | ||
| 495 | ExitOnFailure(hr, "Failed to write dependency registration action to message buffer."); | ||
| 496 | |||
| 497 | // send message | ||
| 498 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult); | ||
| 499 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 500 | |||
| 501 | hr = (HRESULT)dwResult; | ||
| 502 | |||
| 503 | LExit: | ||
| 504 | ReleaseBuffer(pbData); | ||
| 505 | |||
| 506 | return hr; | ||
| 507 | } | ||
| 508 | |||
| 509 | /******************************************************************* | ||
| 510 | ElevationSaveState - | ||
| 511 | |||
| 512 | *******************************************************************/ | ||
| 513 | HRESULT ElevationSaveState( | ||
| 514 | __in HANDLE hPipe, | ||
| 515 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 516 | __in SIZE_T cbBuffer | ||
| 517 | ) | ||
| 518 | { | ||
| 519 | HRESULT hr = S_OK; | ||
| 520 | DWORD dwResult = 0; | ||
| 521 | |||
| 522 | // send message | ||
| 523 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, (DWORD)cbBuffer, NULL, NULL, &dwResult); | ||
| 524 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 525 | |||
| 526 | hr = (HRESULT)dwResult; | ||
| 527 | |||
| 528 | LExit: | ||
| 529 | return hr; | ||
| 530 | } | ||
| 531 | |||
| 532 | /******************************************************************* | ||
| 533 | ElevationLayoutBundle - | ||
| 534 | |||
| 535 | *******************************************************************/ | ||
| 536 | extern "C" HRESULT ElevationLayoutBundle( | ||
| 537 | __in HANDLE hPipe, | ||
| 538 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 539 | __in_z LPCWSTR wzUnverifiedPath | ||
| 540 | ) | ||
| 541 | { | ||
| 542 | HRESULT hr = S_OK; | ||
| 543 | BYTE* pbData = NULL; | ||
| 544 | SIZE_T cbData = 0; | ||
| 545 | DWORD dwResult = 0; | ||
| 546 | |||
| 547 | // serialize message data | ||
| 548 | hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); | ||
| 549 | ExitOnFailure(hr, "Failed to write layout directory to message buffer."); | ||
| 550 | |||
| 551 | hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); | ||
| 552 | ExitOnFailure(hr, "Failed to write payload unverified path to message buffer."); | ||
| 553 | |||
| 554 | // send message | ||
| 555 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, pbData, cbData, NULL, NULL, &dwResult); | ||
| 556 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE message to per-machine process."); | ||
| 557 | |||
| 558 | hr = (HRESULT)dwResult; | ||
| 559 | |||
| 560 | LExit: | ||
| 561 | ReleaseBuffer(pbData); | ||
| 562 | |||
| 563 | return hr; | ||
| 564 | } | ||
| 565 | |||
| 566 | /******************************************************************* | ||
| 567 | ElevationCacheOrLayoutPayload - | ||
| 568 | |||
| 569 | *******************************************************************/ | ||
| 570 | extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload( | ||
| 571 | __in HANDLE hPipe, | ||
| 572 | __in_opt BURN_CONTAINER* pContainer, | ||
| 573 | __in_opt BURN_PACKAGE* pPackage, | ||
| 574 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 575 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 576 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 577 | __in BOOL fMove | ||
| 578 | ) | ||
| 579 | { | ||
| 580 | HRESULT hr = S_OK; | ||
| 581 | BYTE* pbData = NULL; | ||
| 582 | SIZE_T cbData = 0; | ||
| 583 | DWORD dwResult = 0; | ||
| 584 | |||
| 585 | // serialize message data | ||
| 586 | hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL); | ||
| 587 | ExitOnFailure(hr, "Failed to write container id to message buffer."); | ||
| 588 | |||
| 589 | hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL); | ||
| 590 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 591 | |||
| 592 | hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL); | ||
| 593 | ExitOnFailure(hr, "Failed to write payload id to message buffer."); | ||
| 594 | |||
| 595 | hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory); | ||
| 596 | ExitOnFailure(hr, "Failed to write layout directory to message buffer."); | ||
| 597 | |||
| 598 | hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath); | ||
| 599 | ExitOnFailure(hr, "Failed to write unverified path to message buffer."); | ||
| 600 | |||
| 601 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove); | ||
| 602 | ExitOnFailure(hr, "Failed to write move flag to message buffer."); | ||
| 603 | |||
| 604 | // send message | ||
| 605 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult); | ||
| 606 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD message to per-machine process."); | ||
| 607 | |||
| 608 | hr = (HRESULT)dwResult; | ||
| 609 | |||
| 610 | LExit: | ||
| 611 | ReleaseBuffer(pbData); | ||
| 612 | |||
| 613 | return hr; | ||
| 614 | } | ||
| 615 | |||
| 616 | /******************************************************************* | ||
| 617 | ElevationCacheCleanup - | ||
| 618 | |||
| 619 | *******************************************************************/ | ||
| 620 | extern "C" HRESULT ElevationCacheCleanup( | ||
| 621 | __in HANDLE hPipe | ||
| 622 | ) | ||
| 623 | { | ||
| 624 | HRESULT hr = S_OK; | ||
| 625 | DWORD dwResult = 0; | ||
| 626 | |||
| 627 | // send message | ||
| 628 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult); | ||
| 629 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process."); | ||
| 630 | |||
| 631 | hr = (HRESULT)dwResult; | ||
| 632 | |||
| 633 | LExit: | ||
| 634 | return hr; | ||
| 635 | } | ||
| 636 | |||
| 637 | extern "C" HRESULT ElevationProcessDependentRegistration( | ||
| 638 | __in HANDLE hPipe, | ||
| 639 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 640 | ) | ||
| 641 | { | ||
| 642 | HRESULT hr = S_OK; | ||
| 643 | BYTE* pbData = NULL; | ||
| 644 | SIZE_T cbData = 0; | ||
| 645 | DWORD dwResult = 0; | ||
| 646 | |||
| 647 | // serialize message data | ||
| 648 | hr = BuffWriteNumber(&pbData, &cbData, pAction->type); | ||
| 649 | ExitOnFailure(hr, "Failed to write action type to message buffer."); | ||
| 650 | |||
| 651 | hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId); | ||
| 652 | ExitOnFailure(hr, "Failed to write bundle id to message buffer."); | ||
| 653 | |||
| 654 | hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey); | ||
| 655 | ExitOnFailure(hr, "Failed to write dependent provider key to message buffer."); | ||
| 656 | |||
| 657 | // send message | ||
| 658 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult); | ||
| 659 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process."); | ||
| 660 | |||
| 661 | hr = (HRESULT)dwResult; | ||
| 662 | |||
| 663 | LExit: | ||
| 664 | ReleaseBuffer(pbData); | ||
| 665 | |||
| 666 | return hr; | ||
| 667 | } | ||
| 668 | |||
| 669 | /******************************************************************* | ||
| 670 | ElevationExecuteExePackage - | ||
| 671 | |||
| 672 | *******************************************************************/ | ||
| 673 | extern "C" HRESULT ElevationExecuteExePackage( | ||
| 674 | __in HANDLE hPipe, | ||
| 675 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 676 | __in BURN_VARIABLES* pVariables, | ||
| 677 | __in BOOL fRollback, | ||
| 678 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 679 | __in LPVOID pvContext, | ||
| 680 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 681 | ) | ||
| 682 | { | ||
| 683 | HRESULT hr = S_OK; | ||
| 684 | BYTE* pbData = NULL; | ||
| 685 | SIZE_T cbData = 0; | ||
| 686 | BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; | ||
| 687 | DWORD dwResult = 0; | ||
| 688 | |||
| 689 | // serialize message data | ||
| 690 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId); | ||
| 691 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 692 | |||
| 693 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action); | ||
| 694 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 695 | |||
| 696 | hr = BuffWriteNumber(&pbData, &cbData, fRollback); | ||
| 697 | ExitOnFailure(hr, "Failed to write rollback."); | ||
| 698 | |||
| 699 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies); | ||
| 700 | ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer."); | ||
| 701 | |||
| 702 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); | ||
| 703 | ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); | ||
| 704 | |||
| 705 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 706 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 707 | |||
| 708 | // send message | ||
| 709 | context.pfnGenericMessageHandler = pfnGenericMessageHandler; | ||
| 710 | context.pvContext = pvContext; | ||
| 711 | |||
| 712 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); | ||
| 713 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process."); | ||
| 714 | |||
| 715 | hr = ProcessResult(dwResult, pRestart); | ||
| 716 | |||
| 717 | LExit: | ||
| 718 | ReleaseBuffer(pbData); | ||
| 719 | |||
| 720 | return hr; | ||
| 721 | } | ||
| 722 | |||
| 723 | extern "C" HRESULT ElevationMsiBeginTransaction( | ||
| 724 | __in HANDLE hPipe, | ||
| 725 | __in_opt HWND hwndParent, | ||
| 726 | __in LPVOID pvContext | ||
| 727 | ) | ||
| 728 | { | ||
| 729 | UNREFERENCED_PARAMETER(hwndParent); | ||
| 730 | HRESULT hr = S_OK; | ||
| 731 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; | ||
| 732 | DWORD dwResult = ERROR_SUCCESS; | ||
| 733 | |||
| 734 | context.pvContext = pvContext; | ||
| 735 | |||
| 736 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_BEGIN, NULL, 0, NULL, &context, &dwResult); | ||
| 737 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); | ||
| 738 | ExitOnWin32Error(dwResult, hr, "Failed beginning an elevated MSI transaction"); | ||
| 739 | |||
| 740 | LExit: | ||
| 741 | return hr; | ||
| 742 | } | ||
| 743 | |||
| 744 | extern "C" HRESULT ElevationMsiCommitTransaction( | ||
| 745 | __in HANDLE hPipe, | ||
| 746 | __in_opt HWND hwndParent, | ||
| 747 | __in LPVOID pvContext | ||
| 748 | ) | ||
| 749 | { | ||
| 750 | UNREFERENCED_PARAMETER(hwndParent); | ||
| 751 | HRESULT hr = S_OK; | ||
| 752 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; | ||
| 753 | DWORD dwResult = ERROR_SUCCESS; | ||
| 754 | |||
| 755 | context.pvContext = pvContext; | ||
| 756 | |||
| 757 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_COMMIT, NULL, 0, NULL, &context, &dwResult); | ||
| 758 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); | ||
| 759 | ExitOnWin32Error(dwResult, hr, "Failed committing an elevated MSI transaction"); | ||
| 760 | |||
| 761 | LExit: | ||
| 762 | return hr; | ||
| 763 | } | ||
| 764 | |||
| 765 | extern "C" HRESULT ElevationMsiRollbackTransaction( | ||
| 766 | __in HANDLE hPipe, | ||
| 767 | __in_opt HWND hwndParent, | ||
| 768 | __in LPVOID pvContext | ||
| 769 | ) | ||
| 770 | { | ||
| 771 | UNREFERENCED_PARAMETER(hwndParent); | ||
| 772 | HRESULT hr = S_OK; | ||
| 773 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {}; | ||
| 774 | DWORD dwResult = ERROR_SUCCESS; | ||
| 775 | |||
| 776 | context.pvContext = pvContext; | ||
| 777 | |||
| 778 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_ROLLBACK, NULL, 0, NULL, &context, &dwResult); | ||
| 779 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); | ||
| 780 | ExitOnWin32Error(dwResult, hr, "Failed rolling back an elevated MSI transaction"); | ||
| 781 | |||
| 782 | LExit: | ||
| 783 | return hr; | ||
| 784 | } | ||
| 785 | |||
| 786 | |||
| 787 | |||
| 788 | /******************************************************************* | ||
| 789 | ElevationExecuteMsiPackage - | ||
| 790 | |||
| 791 | *******************************************************************/ | ||
| 792 | extern "C" HRESULT ElevationExecuteMsiPackage( | ||
| 793 | __in HANDLE hPipe, | ||
| 794 | __in_opt HWND hwndParent, | ||
| 795 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 796 | __in BURN_VARIABLES* pVariables, | ||
| 797 | __in BOOL fRollback, | ||
| 798 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 799 | __in LPVOID pvContext, | ||
| 800 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 801 | ) | ||
| 802 | { | ||
| 803 | HRESULT hr = S_OK; | ||
| 804 | BYTE* pbData = NULL; | ||
| 805 | SIZE_T cbData = 0; | ||
| 806 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; | ||
| 807 | DWORD dwResult = 0; | ||
| 808 | |||
| 809 | // serialize message data | ||
| 810 | // TODO: for patching we might not have a package | ||
| 811 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId); | ||
| 812 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 813 | |||
| 814 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); | ||
| 815 | ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); | ||
| 816 | |||
| 817 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath); | ||
| 818 | ExitOnFailure(hr, "Failed to write package log to message buffer."); | ||
| 819 | |||
| 820 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel); | ||
| 821 | ExitOnFailure(hr, "Failed to write UI level to message buffer."); | ||
| 822 | |||
| 823 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action); | ||
| 824 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 825 | |||
| 826 | // Feature actions. | ||
| 827 | for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i) | ||
| 828 | { | ||
| 829 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]); | ||
| 830 | ExitOnFailure(hr, "Failed to write feature action to message buffer."); | ||
| 831 | } | ||
| 832 | |||
| 833 | // Slipstream patches actions. | ||
| 834 | for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 835 | { | ||
| 836 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]); | ||
| 837 | ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer."); | ||
| 838 | } | ||
| 839 | |||
| 840 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 841 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 842 | |||
| 843 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); | ||
| 844 | ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); | ||
| 845 | |||
| 846 | |||
| 847 | // send message | ||
| 848 | context.pfnMessageHandler = pfnMessageHandler; | ||
| 849 | context.pvContext = pvContext; | ||
| 850 | |||
| 851 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); | ||
| 852 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process."); | ||
| 853 | |||
| 854 | hr = ProcessResult(dwResult, pRestart); | ||
| 855 | |||
| 856 | LExit: | ||
| 857 | ReleaseBuffer(pbData); | ||
| 858 | |||
| 859 | return hr; | ||
| 860 | } | ||
| 861 | |||
| 862 | /******************************************************************* | ||
| 863 | ElevationExecuteMspPackage - | ||
| 864 | |||
| 865 | *******************************************************************/ | ||
| 866 | extern "C" HRESULT ElevationExecuteMspPackage( | ||
| 867 | __in HANDLE hPipe, | ||
| 868 | __in_opt HWND hwndParent, | ||
| 869 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 870 | __in BURN_VARIABLES* pVariables, | ||
| 871 | __in BOOL fRollback, | ||
| 872 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 873 | __in LPVOID pvContext, | ||
| 874 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 875 | ) | ||
| 876 | { | ||
| 877 | HRESULT hr = S_OK; | ||
| 878 | BYTE* pbData = NULL; | ||
| 879 | SIZE_T cbData = 0; | ||
| 880 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { }; | ||
| 881 | DWORD dwResult = 0; | ||
| 882 | |||
| 883 | // serialize message data | ||
| 884 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId); | ||
| 885 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 886 | |||
| 887 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent); | ||
| 888 | ExitOnFailure(hr, "Failed to write parent hwnd to message buffer."); | ||
| 889 | |||
| 890 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode); | ||
| 891 | ExitOnFailure(hr, "Failed to write target product code to message buffer."); | ||
| 892 | |||
| 893 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath); | ||
| 894 | ExitOnFailure(hr, "Failed to write package log to message buffer."); | ||
| 895 | |||
| 896 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel); | ||
| 897 | ExitOnFailure(hr, "Failed to write UI level to message buffer."); | ||
| 898 | |||
| 899 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action); | ||
| 900 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 901 | |||
| 902 | hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches); | ||
| 903 | ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer."); | ||
| 904 | |||
| 905 | for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) | ||
| 906 | { | ||
| 907 | hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].dwOrder); | ||
| 908 | ExitOnFailure(hr, "Failed to write ordered patch order to message buffer."); | ||
| 909 | |||
| 910 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId); | ||
| 911 | ExitOnFailure(hr, "Failed to write ordered patch id to message buffer."); | ||
| 912 | } | ||
| 913 | |||
| 914 | hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); | ||
| 915 | ExitOnFailure(hr, "Failed to write variables."); | ||
| 916 | |||
| 917 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback); | ||
| 918 | ExitOnFailure(hr, "Failed to write rollback flag to message buffer."); | ||
| 919 | |||
| 920 | // send message | ||
| 921 | context.pfnMessageHandler = pfnMessageHandler; | ||
| 922 | context.pvContext = pvContext; | ||
| 923 | |||
| 924 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult); | ||
| 925 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process."); | ||
| 926 | |||
| 927 | hr = ProcessResult(dwResult, pRestart); | ||
| 928 | |||
| 929 | LExit: | ||
| 930 | ReleaseBuffer(pbData); | ||
| 931 | |||
| 932 | return hr; | ||
| 933 | } | ||
| 934 | |||
| 935 | /******************************************************************* | ||
| 936 | ElevationExecuteMsuPackage - | ||
| 937 | |||
| 938 | *******************************************************************/ | ||
| 939 | extern "C" HRESULT ElevationExecuteMsuPackage( | ||
| 940 | __in HANDLE hPipe, | ||
| 941 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 942 | __in BOOL fRollback, | ||
| 943 | __in BOOL fStopWusaService, | ||
| 944 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 945 | __in LPVOID pvContext, | ||
| 946 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 947 | ) | ||
| 948 | { | ||
| 949 | HRESULT hr = S_OK; | ||
| 950 | BYTE* pbData = NULL; | ||
| 951 | SIZE_T cbData = 0; | ||
| 952 | BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { }; | ||
| 953 | DWORD dwResult = 0; | ||
| 954 | |||
| 955 | // serialize message data | ||
| 956 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId); | ||
| 957 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 958 | |||
| 959 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath); | ||
| 960 | ExitOnFailure(hr, "Failed to write package log to message buffer."); | ||
| 961 | |||
| 962 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action); | ||
| 963 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 964 | |||
| 965 | hr = BuffWriteNumber(&pbData, &cbData, fRollback); | ||
| 966 | ExitOnFailure(hr, "Failed to write rollback."); | ||
| 967 | |||
| 968 | hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService); | ||
| 969 | ExitOnFailure(hr, "Failed to write StopWusaService."); | ||
| 970 | |||
| 971 | // send message | ||
| 972 | context.pfnGenericMessageHandler = pfnGenericMessageHandler; | ||
| 973 | context.pvContext = pvContext; | ||
| 974 | |||
| 975 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult); | ||
| 976 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process."); | ||
| 977 | |||
| 978 | hr = ProcessResult(dwResult, pRestart); | ||
| 979 | |||
| 980 | LExit: | ||
| 981 | ReleaseBuffer(pbData); | ||
| 982 | |||
| 983 | return hr; | ||
| 984 | } | ||
| 985 | |||
| 986 | extern "C" HRESULT ElevationExecutePackageProviderAction( | ||
| 987 | __in HANDLE hPipe, | ||
| 988 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 989 | ) | ||
| 990 | { | ||
| 991 | HRESULT hr = S_OK; | ||
| 992 | BYTE* pbData = NULL; | ||
| 993 | SIZE_T cbData = 0; | ||
| 994 | DWORD dwResult = 0; | ||
| 995 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 996 | |||
| 997 | // Serialize the message data. | ||
| 998 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId); | ||
| 999 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 1000 | |||
| 1001 | hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action); | ||
| 1002 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 1003 | |||
| 1004 | // Send the message. | ||
| 1005 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult); | ||
| 1006 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process."); | ||
| 1007 | |||
| 1008 | // Ignore the restart since this action only results in registry writes. | ||
| 1009 | hr = ProcessResult(dwResult, &restart); | ||
| 1010 | |||
| 1011 | LExit: | ||
| 1012 | ReleaseBuffer(pbData); | ||
| 1013 | |||
| 1014 | return hr; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | extern "C" HRESULT ElevationExecutePackageDependencyAction( | ||
| 1018 | __in HANDLE hPipe, | ||
| 1019 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 1020 | ) | ||
| 1021 | { | ||
| 1022 | HRESULT hr = S_OK; | ||
| 1023 | BYTE* pbData = NULL; | ||
| 1024 | SIZE_T cbData = 0; | ||
| 1025 | DWORD dwResult = 0; | ||
| 1026 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 1027 | |||
| 1028 | // Serialize the message data. | ||
| 1029 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId); | ||
| 1030 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 1031 | |||
| 1032 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey); | ||
| 1033 | ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer."); | ||
| 1034 | |||
| 1035 | hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action); | ||
| 1036 | ExitOnFailure(hr, "Failed to write action to message buffer."); | ||
| 1037 | |||
| 1038 | // Send the message. | ||
| 1039 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult); | ||
| 1040 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process."); | ||
| 1041 | |||
| 1042 | // Ignore the restart since this action only results in registry writes. | ||
| 1043 | hr = ProcessResult(dwResult, &restart); | ||
| 1044 | |||
| 1045 | LExit: | ||
| 1046 | ReleaseBuffer(pbData); | ||
| 1047 | |||
| 1048 | return hr; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | /******************************************************************* | ||
| 1052 | ElevationLoadCompatiblePackageAction - Load compatible package | ||
| 1053 | information from the referenced package. | ||
| 1054 | |||
| 1055 | *******************************************************************/ | ||
| 1056 | extern "C" HRESULT ElevationLoadCompatiblePackageAction( | ||
| 1057 | __in HANDLE hPipe, | ||
| 1058 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 1059 | ) | ||
| 1060 | { | ||
| 1061 | HRESULT hr = S_OK; | ||
| 1062 | BYTE* pbData = NULL; | ||
| 1063 | SIZE_T cbData = 0; | ||
| 1064 | DWORD dwResult = 0; | ||
| 1065 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 1066 | |||
| 1067 | // Serialize message data. | ||
| 1068 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pReferencePackage->sczId); | ||
| 1069 | ExitOnFailure(hr, "Failed to write package id to message buffer."); | ||
| 1070 | |||
| 1071 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode); | ||
| 1072 | ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer."); | ||
| 1073 | |||
| 1074 | hr = BuffWriteNumber64(&pbData, &cbData, pExecuteAction->compatiblePackage.qwInstalledVersion); | ||
| 1075 | ExitOnFailure(hr, "Failed to write installed version to message buffer."); | ||
| 1076 | |||
| 1077 | // Send the message. | ||
| 1078 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); | ||
| 1079 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE message to per-machine process."); | ||
| 1080 | |||
| 1081 | // Ignore the restart since this action only loads data into memory. | ||
| 1082 | hr = ProcessResult(dwResult, &restart); | ||
| 1083 | |||
| 1084 | LExit: | ||
| 1085 | ReleaseBuffer(pbData); | ||
| 1086 | |||
| 1087 | return hr; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | /******************************************************************* | ||
| 1091 | ElevationCleanPackage - | ||
| 1092 | |||
| 1093 | *******************************************************************/ | ||
| 1094 | extern "C" HRESULT ElevationCleanPackage( | ||
| 1095 | __in HANDLE hPipe, | ||
| 1096 | __in BURN_PACKAGE* pPackage | ||
| 1097 | ) | ||
| 1098 | { | ||
| 1099 | HRESULT hr = S_OK; | ||
| 1100 | BYTE* pbData = NULL; | ||
| 1101 | SIZE_T cbData = 0; | ||
| 1102 | DWORD dwResult = 0; | ||
| 1103 | |||
| 1104 | // serialize message data | ||
| 1105 | hr = BuffWriteString(&pbData, &cbData, pPackage->sczId); | ||
| 1106 | ExitOnFailure(hr, "Failed to write clean package id to message buffer."); | ||
| 1107 | |||
| 1108 | // send message | ||
| 1109 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult); | ||
| 1110 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process."); | ||
| 1111 | |||
| 1112 | hr = (HRESULT)dwResult; | ||
| 1113 | |||
| 1114 | LExit: | ||
| 1115 | ReleaseBuffer(pbData); | ||
| 1116 | |||
| 1117 | return hr; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | extern "C" HRESULT ElevationLaunchApprovedExe( | ||
| 1121 | __in HANDLE hPipe, | ||
| 1122 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
| 1123 | __out DWORD* pdwProcessId | ||
| 1124 | ) | ||
| 1125 | { | ||
| 1126 | HRESULT hr = S_OK; | ||
| 1127 | BYTE* pbData = NULL; | ||
| 1128 | SIZE_T cbData = 0; | ||
| 1129 | DWORD dwResult = 0; | ||
| 1130 | BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { }; | ||
| 1131 | |||
| 1132 | // Serialize message data. | ||
| 1133 | hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId); | ||
| 1134 | ExitOnFailure(hr, "Failed to write approved exe id to message buffer."); | ||
| 1135 | |||
| 1136 | hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments); | ||
| 1137 | ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer."); | ||
| 1138 | |||
| 1139 | hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout); | ||
| 1140 | ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer."); | ||
| 1141 | |||
| 1142 | // Send the message. | ||
| 1143 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult); | ||
| 1144 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process."); | ||
| 1145 | |||
| 1146 | hr = (HRESULT)dwResult; | ||
| 1147 | *pdwProcessId = context.dwProcessId; | ||
| 1148 | |||
| 1149 | LExit: | ||
| 1150 | ReleaseBuffer(pbData); | ||
| 1151 | |||
| 1152 | return hr; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | /******************************************************************* | ||
| 1156 | ElevationChildPumpMessages - | ||
| 1157 | |||
| 1158 | *******************************************************************/ | ||
| 1159 | extern "C" HRESULT ElevationChildPumpMessages( | ||
| 1160 | __in DWORD dwLoggingTlsId, | ||
| 1161 | __in HANDLE hPipe, | ||
| 1162 | __in HANDLE hCachePipe, | ||
| 1163 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 1164 | __in BURN_CONTAINERS* pContainers, | ||
| 1165 | __in BURN_PACKAGES* pPackages, | ||
| 1166 | __in BURN_PAYLOADS* pPayloads, | ||
| 1167 | __in BURN_VARIABLES* pVariables, | ||
| 1168 | __in BURN_REGISTRATION* pRegistration, | ||
| 1169 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1170 | __out HANDLE* phLock, | ||
| 1171 | __out BOOL* pfDisabledAutomaticUpdates, | ||
| 1172 | __out DWORD* pdwChildExitCode, | ||
| 1173 | __out BOOL* pfRestart | ||
| 1174 | ) | ||
| 1175 | { | ||
| 1176 | HRESULT hr = S_OK; | ||
| 1177 | BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { }; | ||
| 1178 | BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { }; | ||
| 1179 | HANDLE hCacheThread = NULL; | ||
| 1180 | BURN_PIPE_RESULT result = { }; | ||
| 1181 | |||
| 1182 | cacheContext.dwLoggingTlsId = dwLoggingTlsId; | ||
| 1183 | cacheContext.hPipe = hCachePipe; | ||
| 1184 | cacheContext.pContainers = pContainers; | ||
| 1185 | cacheContext.pPackages = pPackages; | ||
| 1186 | cacheContext.pPayloads = pPayloads; | ||
| 1187 | cacheContext.pVariables = pVariables; | ||
| 1188 | cacheContext.pRegistration = pRegistration; | ||
| 1189 | cacheContext.pUserExperience = pUserExperience; | ||
| 1190 | |||
| 1191 | context.dwLoggingTlsId = dwLoggingTlsId; | ||
| 1192 | context.hPipe = hPipe; | ||
| 1193 | context.phLock = phLock; | ||
| 1194 | context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates; | ||
| 1195 | context.pApprovedExes = pApprovedExes; | ||
| 1196 | context.pContainers = pContainers; | ||
| 1197 | context.pPackages = pPackages; | ||
| 1198 | context.pPayloads = pPayloads; | ||
| 1199 | context.pVariables = pVariables; | ||
| 1200 | context.pRegistration = pRegistration; | ||
| 1201 | context.pUserExperience = pUserExperience; | ||
| 1202 | |||
| 1203 | hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL); | ||
| 1204 | ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread."); | ||
| 1205 | |||
| 1206 | hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result); | ||
| 1207 | ExitOnFailure(hr, "Failed to pump messages in child process."); | ||
| 1208 | |||
| 1209 | // Wait for the cache thread and verify it gets the right result but don't fail if things | ||
| 1210 | // don't work out. | ||
| 1211 | WaitForElevatedChildCacheThread(hCacheThread, result.dwResult); | ||
| 1212 | |||
| 1213 | *pdwChildExitCode = result.dwResult; | ||
| 1214 | *pfRestart = result.fRestart; | ||
| 1215 | |||
| 1216 | LExit: | ||
| 1217 | ReleaseHandle(hCacheThread); | ||
| 1218 | |||
| 1219 | return hr; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | extern "C" HRESULT ElevationChildResumeAutomaticUpdates() | ||
| 1223 | { | ||
| 1224 | HRESULT hr = S_OK; | ||
| 1225 | |||
| 1226 | LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING); | ||
| 1227 | |||
| 1228 | hr = WuaResumeAutomaticUpdates(); | ||
| 1229 | ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing..."); | ||
| 1230 | |||
| 1231 | LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED); | ||
| 1232 | |||
| 1233 | LExit: | ||
| 1234 | return hr; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | // internal function definitions | ||
| 1238 | |||
| 1239 | static DWORD WINAPI ElevatedChildCacheThreadProc( | ||
| 1240 | __in LPVOID lpThreadParameter | ||
| 1241 | ) | ||
| 1242 | { | ||
| 1243 | HRESULT hr = S_OK; | ||
| 1244 | BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(lpThreadParameter); | ||
| 1245 | BOOL fComInitialized = FALSE; | ||
| 1246 | BURN_PIPE_RESULT result = { }; | ||
| 1247 | |||
| 1248 | if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe)) | ||
| 1249 | { | ||
| 1250 | ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging."); | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | // initialize COM | ||
| 1254 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 1255 | ExitOnFailure(hr, "Failed to initialize COM."); | ||
| 1256 | fComInitialized = TRUE; | ||
| 1257 | |||
| 1258 | hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result); | ||
| 1259 | ExitOnFailure(hr, "Failed to pump messages in child process."); | ||
| 1260 | |||
| 1261 | hr = (HRESULT)result.dwResult; | ||
| 1262 | |||
| 1263 | LExit: | ||
| 1264 | if (fComInitialized) | ||
| 1265 | { | ||
| 1266 | ::CoUninitialize(); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | return (DWORD)hr; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | static HRESULT WaitForElevatedChildCacheThread( | ||
| 1273 | __in HANDLE hCacheThread, | ||
| 1274 | __in DWORD dwExpectedExitCode | ||
| 1275 | ) | ||
| 1276 | { | ||
| 1277 | UNREFERENCED_PARAMETER(dwExpectedExitCode); | ||
| 1278 | |||
| 1279 | HRESULT hr = S_OK; | ||
| 1280 | DWORD dwExitCode = ERROR_SUCCESS; | ||
| 1281 | |||
| 1282 | if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT)) | ||
| 1283 | { | ||
| 1284 | ExitWithLastError(hr, "Failed to wait for cache thread to terminate."); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | if (!::GetExitCodeThread(hCacheThread, &dwExitCode)) | ||
| 1288 | { | ||
| 1289 | ExitWithLastError(hr, "Failed to get cache thread exit code."); | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code."); | ||
| 1293 | |||
| 1294 | LExit: | ||
| 1295 | return hr; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | static HRESULT ProcessGenericExecuteMessages( | ||
| 1299 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 1300 | __in_opt LPVOID pvContext, | ||
| 1301 | __out DWORD* pdwResult | ||
| 1302 | ) | ||
| 1303 | { | ||
| 1304 | HRESULT hr = S_OK; | ||
| 1305 | SIZE_T iData = 0; | ||
| 1306 | BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT*>(pvContext); | ||
| 1307 | LPWSTR sczMessage = NULL; | ||
| 1308 | DWORD cFiles = 0; | ||
| 1309 | LPWSTR* rgwzFiles = NULL; | ||
| 1310 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 1311 | |||
| 1312 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults); | ||
| 1313 | ExitOnFailure(hr, "Failed to allowed results."); | ||
| 1314 | |||
| 1315 | // Process the message. | ||
| 1316 | switch (pMsg->dwMessage) | ||
| 1317 | { | ||
| 1318 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: | ||
| 1319 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 1320 | |||
| 1321 | // read message parameters | ||
| 1322 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); | ||
| 1323 | ExitOnFailure(hr, "Failed to progress."); | ||
| 1324 | break; | ||
| 1325 | |||
| 1326 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: | ||
| 1327 | message.type = GENERIC_EXECUTE_MESSAGE_ERROR; | ||
| 1328 | |||
| 1329 | // read message parameters | ||
| 1330 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); | ||
| 1331 | ExitOnFailure(hr, "Failed to read error code."); | ||
| 1332 | |||
| 1333 | hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); | ||
| 1334 | ExitOnFailure(hr, "Failed to read message."); | ||
| 1335 | |||
| 1336 | message.error.wzMessage = sczMessage; | ||
| 1337 | break; | ||
| 1338 | |||
| 1339 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: | ||
| 1340 | message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; | ||
| 1341 | |||
| 1342 | // read message parameters | ||
| 1343 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles); | ||
| 1344 | ExitOnFailure(hr, "Failed to read file count."); | ||
| 1345 | |||
| 1346 | rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); | ||
| 1347 | ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use."); | ||
| 1348 | |||
| 1349 | for (DWORD i = 0; i < cFiles; ++i) | ||
| 1350 | { | ||
| 1351 | hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]); | ||
| 1352 | ExitOnFailure(hr, "Failed to read file name: %u", i); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | message.filesInUse.cFiles = cFiles; | ||
| 1356 | message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; | ||
| 1357 | break; | ||
| 1358 | |||
| 1359 | default: | ||
| 1360 | hr = E_INVALIDARG; | ||
| 1361 | ExitOnRootFailure(hr, "Invalid package message."); | ||
| 1362 | break; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | // send message | ||
| 1366 | *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);; | ||
| 1367 | |||
| 1368 | LExit: | ||
| 1369 | ReleaseStr(sczMessage); | ||
| 1370 | |||
| 1371 | if (rgwzFiles) | ||
| 1372 | { | ||
| 1373 | for (DWORD i = 0; i < cFiles; ++i) | ||
| 1374 | { | ||
| 1375 | ReleaseStr(rgwzFiles[i]); | ||
| 1376 | } | ||
| 1377 | MemFree(rgwzFiles); | ||
| 1378 | } | ||
| 1379 | return hr; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | static HRESULT ProcessMsiPackageMessages( | ||
| 1383 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 1384 | __in_opt LPVOID pvContext, | ||
| 1385 | __out DWORD* pdwResult | ||
| 1386 | ) | ||
| 1387 | { | ||
| 1388 | HRESULT hr = S_OK; | ||
| 1389 | SIZE_T iData = 0; | ||
| 1390 | WIU_MSI_EXECUTE_MESSAGE message = { }; | ||
| 1391 | DWORD cMsiData = 0; | ||
| 1392 | LPWSTR* rgwzMsiData = NULL; | ||
| 1393 | BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext); | ||
| 1394 | LPWSTR sczMessage = NULL; | ||
| 1395 | |||
| 1396 | // Read MSI extended message data. | ||
| 1397 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData); | ||
| 1398 | ExitOnFailure(hr, "Failed to read MSI data count."); | ||
| 1399 | |||
| 1400 | if (cMsiData) | ||
| 1401 | { | ||
| 1402 | rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE); | ||
| 1403 | ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data."); | ||
| 1404 | |||
| 1405 | for (DWORD i = 0; i < cMsiData; ++i) | ||
| 1406 | { | ||
| 1407 | hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]); | ||
| 1408 | ExitOnFailure(hr, "Failed to read MSI data: %u", i); | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | message.cData = cMsiData; | ||
| 1412 | message.rgwzData = (LPCWSTR*)rgwzMsiData; | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.dwAllowedResults); | ||
| 1416 | ExitOnFailure(hr, "Failed to read UI flags."); | ||
| 1417 | |||
| 1418 | // Process the rest of the message. | ||
| 1419 | switch (pMsg->dwMessage) | ||
| 1420 | { | ||
| 1421 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS: | ||
| 1422 | // read message parameters | ||
| 1423 | message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; | ||
| 1424 | |||
| 1425 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage); | ||
| 1426 | ExitOnFailure(hr, "Failed to read progress."); | ||
| 1427 | break; | ||
| 1428 | |||
| 1429 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR: | ||
| 1430 | // read message parameters | ||
| 1431 | message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; | ||
| 1432 | |||
| 1433 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode); | ||
| 1434 | ExitOnFailure(hr, "Failed to read error code."); | ||
| 1435 | |||
| 1436 | hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); | ||
| 1437 | ExitOnFailure(hr, "Failed to read message."); | ||
| 1438 | message.error.wzMessage = sczMessage; | ||
| 1439 | break; | ||
| 1440 | |||
| 1441 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE: | ||
| 1442 | // read message parameters | ||
| 1443 | message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; | ||
| 1444 | |||
| 1445 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt); | ||
| 1446 | ExitOnFailure(hr, "Failed to read message type."); | ||
| 1447 | |||
| 1448 | hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage); | ||
| 1449 | ExitOnFailure(hr, "Failed to read message."); | ||
| 1450 | message.msiMessage.wzMessage = sczMessage; | ||
| 1451 | break; | ||
| 1452 | |||
| 1453 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE: | ||
| 1454 | message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; | ||
| 1455 | message.msiFilesInUse.cFiles = cMsiData; | ||
| 1456 | message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData; | ||
| 1457 | break; | ||
| 1458 | |||
| 1459 | default: | ||
| 1460 | hr = E_INVALIDARG; | ||
| 1461 | ExitOnRootFailure(hr, "Invalid package message."); | ||
| 1462 | break; | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | // send message | ||
| 1466 | *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext); | ||
| 1467 | |||
| 1468 | LExit: | ||
| 1469 | ReleaseStr(sczMessage); | ||
| 1470 | |||
| 1471 | if (rgwzMsiData) | ||
| 1472 | { | ||
| 1473 | for (DWORD i = 0; i < cMsiData; ++i) | ||
| 1474 | { | ||
| 1475 | ReleaseStr(rgwzMsiData[i]); | ||
| 1476 | } | ||
| 1477 | |||
| 1478 | MemFree(rgwzMsiData); | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | return hr; | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | static HRESULT ProcessLaunchApprovedExeMessages( | ||
| 1485 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 1486 | __in_opt LPVOID pvContext, | ||
| 1487 | __out DWORD* pdwResult | ||
| 1488 | ) | ||
| 1489 | { | ||
| 1490 | HRESULT hr = S_OK; | ||
| 1491 | SIZE_T iData = 0; | ||
| 1492 | BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT*>(pvContext); | ||
| 1493 | DWORD dwProcessId = 0; | ||
| 1494 | |||
| 1495 | // Process the message. | ||
| 1496 | switch (pMsg->dwMessage) | ||
| 1497 | { | ||
| 1498 | case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID: | ||
| 1499 | // read message parameters | ||
| 1500 | hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId); | ||
| 1501 | ExitOnFailure(hr, "Failed to read approved exe process id."); | ||
| 1502 | pContext->dwProcessId = dwProcessId; | ||
| 1503 | break; | ||
| 1504 | |||
| 1505 | default: | ||
| 1506 | hr = E_INVALIDARG; | ||
| 1507 | ExitOnRootFailure(hr, "Invalid launch approved exe message."); | ||
| 1508 | break; | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | *pdwResult = static_cast<DWORD>(hr); | ||
| 1512 | |||
| 1513 | LExit: | ||
| 1514 | return hr; | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | static HRESULT ProcessElevatedChildMessage( | ||
| 1518 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 1519 | __in_opt LPVOID pvContext, | ||
| 1520 | __out DWORD* pdwResult | ||
| 1521 | ) | ||
| 1522 | { | ||
| 1523 | HRESULT hr = S_OK; | ||
| 1524 | BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext); | ||
| 1525 | HRESULT hrResult = S_OK; | ||
| 1526 | DWORD dwPid = 0; | ||
| 1527 | |||
| 1528 | switch (pMsg->dwMessage) | ||
| 1529 | { | ||
| 1530 | case BURN_ELEVATION_TRANSACTION_BEGIN: | ||
| 1531 | hrResult = OnMsiBeginTransaction(pContext); | ||
| 1532 | break; | ||
| 1533 | |||
| 1534 | case BURN_ELEVATION_TRANSACTION_COMMIT: | ||
| 1535 | hrResult = OnMsiCommitTransaction(pContext); | ||
| 1536 | break; | ||
| 1537 | |||
| 1538 | case BURN_ELEVATION_TRANSACTION_ROLLBACK: | ||
| 1539 | hrResult = OnMsiRollbackTransaction(pContext); | ||
| 1540 | break; | ||
| 1541 | |||
| 1542 | case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: | ||
| 1543 | hrResult = OnApplyInitialize(pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1544 | break; | ||
| 1545 | |||
| 1546 | case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: | ||
| 1547 | hrResult = OnApplyUninitialize(pContext->phLock); | ||
| 1548 | break; | ||
| 1549 | |||
| 1550 | case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN: | ||
| 1551 | hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, pContext->pUserExperience, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1552 | break; | ||
| 1553 | |||
| 1554 | case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME: | ||
| 1555 | hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1556 | break; | ||
| 1557 | |||
| 1558 | case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END: | ||
| 1559 | hrResult = OnSessionEnd(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1560 | break; | ||
| 1561 | |||
| 1562 | case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE: | ||
| 1563 | hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1564 | break; | ||
| 1565 | |||
| 1566 | case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION: | ||
| 1567 | hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1568 | break; | ||
| 1569 | |||
| 1570 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE: | ||
| 1571 | hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1572 | break; | ||
| 1573 | |||
| 1574 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE: | ||
| 1575 | hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1576 | break; | ||
| 1577 | |||
| 1578 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE: | ||
| 1579 | hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1580 | break; | ||
| 1581 | |||
| 1582 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE: | ||
| 1583 | hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1584 | break; | ||
| 1585 | |||
| 1586 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER: | ||
| 1587 | hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1588 | break; | ||
| 1589 | |||
| 1590 | case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY: | ||
| 1591 | hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1592 | break; | ||
| 1593 | |||
| 1594 | case BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE: | ||
| 1595 | hrResult = OnLoadCompatiblePackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1596 | break; | ||
| 1597 | |||
| 1598 | case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: | ||
| 1599 | hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1600 | break; | ||
| 1601 | |||
| 1602 | case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE: | ||
| 1603 | hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1604 | break; | ||
| 1605 | |||
| 1606 | default: | ||
| 1607 | hr = E_INVALIDARG; | ||
| 1608 | ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | *pdwResult = dwPid ? dwPid : (DWORD)hrResult; | ||
| 1612 | |||
| 1613 | LExit: | ||
| 1614 | return hr; | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | static HRESULT ProcessElevatedChildCacheMessage( | ||
| 1618 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 1619 | __in_opt LPVOID pvContext, | ||
| 1620 | __out DWORD* pdwResult | ||
| 1621 | ) | ||
| 1622 | { | ||
| 1623 | HRESULT hr = S_OK; | ||
| 1624 | BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext); | ||
| 1625 | HRESULT hrResult = S_OK; | ||
| 1626 | |||
| 1627 | switch (pMsg->dwMessage) | ||
| 1628 | { | ||
| 1629 | case BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE: | ||
| 1630 | hrResult = OnLayoutBundle(pContext->pRegistration->sczExecutableName, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1631 | break; | ||
| 1632 | |||
| 1633 | case BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD: | ||
| 1634 | hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1635 | break; | ||
| 1636 | |||
| 1637 | case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP: | ||
| 1638 | OnCacheCleanup(pContext->pRegistration->sczId); | ||
| 1639 | hrResult = S_OK; | ||
| 1640 | break; | ||
| 1641 | |||
| 1642 | case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE: | ||
| 1643 | hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData); | ||
| 1644 | break; | ||
| 1645 | |||
| 1646 | default: | ||
| 1647 | hr = E_INVALIDARG; | ||
| 1648 | ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage); | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | *pdwResult = (DWORD)hrResult; | ||
| 1652 | |||
| 1653 | LExit: | ||
| 1654 | return hr; | ||
| 1655 | } | ||
| 1656 | |||
| 1657 | static HRESULT ProcessResult( | ||
| 1658 | __in DWORD dwResult, | ||
| 1659 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 1660 | ) | ||
| 1661 | { | ||
| 1662 | HRESULT hr = static_cast<HRESULT>(dwResult); | ||
| 1663 | if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr) | ||
| 1664 | { | ||
| 1665 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 1666 | hr = S_OK; | ||
| 1667 | } | ||
| 1668 | else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr) | ||
| 1669 | { | ||
| 1670 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
| 1671 | hr = S_OK; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | return hr; | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | static HRESULT OnMsiBeginTransaction( | ||
| 1678 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 1679 | ) | ||
| 1680 | { | ||
| 1681 | UINT uResult = ERROR_SUCCESS; | ||
| 1682 | HRESULT hr = S_OK; | ||
| 1683 | |||
| 1684 | pContext->hMsiTrns = NULL; | ||
| 1685 | pContext->hMsiTrnsEvent = NULL; | ||
| 1686 | uResult = MsiBeginTransaction(L"WiX", 0, &pContext->hMsiTrns, &pContext->hMsiTrnsEvent); | ||
| 1687 | ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction"); | ||
| 1688 | |||
| 1689 | LExit: | ||
| 1690 | return hr; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | static HRESULT OnMsiCommitTransaction( | ||
| 1694 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 1695 | ) | ||
| 1696 | { | ||
| 1697 | UINT uResult = ERROR_SUCCESS; | ||
| 1698 | HRESULT hr = S_OK; | ||
| 1699 | |||
| 1700 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT); | ||
| 1701 | ExitOnWin32Error(uResult, hr, "Failed committing an MSI transaction"); | ||
| 1702 | |||
| 1703 | LExit: | ||
| 1704 | pContext->hMsiTrns = NULL; | ||
| 1705 | pContext->hMsiTrnsEvent = NULL; | ||
| 1706 | return hr; | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | static HRESULT OnMsiRollbackTransaction( | ||
| 1710 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
| 1711 | ) { | ||
| 1712 | UINT uResult = ERROR_SUCCESS; | ||
| 1713 | HRESULT hr = S_OK; | ||
| 1714 | |||
| 1715 | uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK); | ||
| 1716 | ExitOnWin32Error(uResult, hr, "Failed rolling back an MSI transaction"); | ||
| 1717 | |||
| 1718 | LExit: | ||
| 1719 | pContext->hMsiTrns = NULL; | ||
| 1720 | pContext->hMsiTrnsEvent = NULL; | ||
| 1721 | return hr; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | static HRESULT OnApplyInitialize( | ||
| 1725 | __in BURN_VARIABLES* pVariables, | ||
| 1726 | __in BURN_REGISTRATION* pRegistration, | ||
| 1727 | __in HANDLE* phLock, | ||
| 1728 | __in BOOL* pfDisabledWindowsUpdate, | ||
| 1729 | __in BYTE* pbData, | ||
| 1730 | __in DWORD cbData | ||
| 1731 | ) | ||
| 1732 | { | ||
| 1733 | HRESULT hr = S_OK; | ||
| 1734 | SIZE_T iData = 0; | ||
| 1735 | DWORD dwAction = 0; | ||
| 1736 | DWORD dwAUAction = 0; | ||
| 1737 | DWORD dwTakeSystemRestorePoint = 0; | ||
| 1738 | LPWSTR sczBundleName = NULL; | ||
| 1739 | |||
| 1740 | // Deserialize message data. | ||
| 1741 | hr = BuffReadNumber(pbData, cbData, &iData, &dwAction); | ||
| 1742 | ExitOnFailure(hr, "Failed to read action."); | ||
| 1743 | |||
| 1744 | hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction); | ||
| 1745 | ExitOnFailure(hr, "Failed to read update action."); | ||
| 1746 | |||
| 1747 | hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint); | ||
| 1748 | ExitOnFailure(hr, "Failed to read system restore point action."); | ||
| 1749 | |||
| 1750 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 1751 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 1752 | |||
| 1753 | // Initialize. | ||
| 1754 | hr = ApplyLock(TRUE, phLock); | ||
| 1755 | ExitOnFailure(hr, "Failed to acquire lock due to setup in other session."); | ||
| 1756 | |||
| 1757 | // Reset and reload the related bundles. | ||
| 1758 | RelatedBundlesUninitialize(&pRegistration->relatedBundles); | ||
| 1759 | |||
| 1760 | hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); | ||
| 1761 | ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); | ||
| 1762 | |||
| 1763 | // Attempt to pause AU with best effort. | ||
| 1764 | if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) | ||
| 1765 | { | ||
| 1766 | LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING); | ||
| 1767 | |||
| 1768 | hr = WuaPauseAutomaticUpdates(); | ||
| 1769 | if (FAILED(hr)) | ||
| 1770 | { | ||
| 1771 | LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr); | ||
| 1772 | hr = S_OK; | ||
| 1773 | } | ||
| 1774 | else | ||
| 1775 | { | ||
| 1776 | LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED); | ||
| 1777 | if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction) | ||
| 1778 | { | ||
| 1779 | *pfDisabledWindowsUpdate = TRUE; | ||
| 1780 | } | ||
| 1781 | } | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | if (dwTakeSystemRestorePoint) | ||
| 1785 | { | ||
| 1786 | hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName); | ||
| 1787 | if (FAILED(hr)) | ||
| 1788 | { | ||
| 1789 | hr = S_OK; | ||
| 1790 | ExitFunction(); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING); | ||
| 1794 | |||
| 1795 | BOOTSTRAPPER_ACTION action = static_cast<BOOTSTRAPPER_ACTION>(dwAction); | ||
| 1796 | SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY; | ||
| 1797 | hr = SrpCreateRestorePoint(sczBundleName, restoreAction); | ||
| 1798 | if (SUCCEEDED(hr)) | ||
| 1799 | { | ||
| 1800 | LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED); | ||
| 1801 | } | ||
| 1802 | else if (E_NOTIMPL == hr) | ||
| 1803 | { | ||
| 1804 | LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED); | ||
| 1805 | hr = S_OK; | ||
| 1806 | } | ||
| 1807 | else if (FAILED(hr)) | ||
| 1808 | { | ||
| 1809 | LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr); | ||
| 1810 | hr = S_OK; | ||
| 1811 | } | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | LExit: | ||
| 1815 | ReleaseStr(sczBundleName); | ||
| 1816 | return hr; | ||
| 1817 | } | ||
| 1818 | |||
| 1819 | static HRESULT OnApplyUninitialize( | ||
| 1820 | __in HANDLE* phLock | ||
| 1821 | ) | ||
| 1822 | { | ||
| 1823 | Assert(phLock); | ||
| 1824 | |||
| 1825 | // TODO: end system restore point. | ||
| 1826 | |||
| 1827 | if (*phLock) | ||
| 1828 | { | ||
| 1829 | ::ReleaseMutex(*phLock); | ||
| 1830 | ::CloseHandle(*phLock); | ||
| 1831 | *phLock = NULL; | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | return S_OK; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | static HRESULT OnSessionBegin( | ||
| 1838 | __in BURN_REGISTRATION* pRegistration, | ||
| 1839 | __in BURN_VARIABLES* pVariables, | ||
| 1840 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1841 | __in BYTE* pbData, | ||
| 1842 | __in DWORD cbData | ||
| 1843 | ) | ||
| 1844 | { | ||
| 1845 | HRESULT hr = S_OK; | ||
| 1846 | SIZE_T iData = 0; | ||
| 1847 | LPWSTR sczEngineWorkingPath = NULL; | ||
| 1848 | DWORD dwRegistrationOperations = 0; | ||
| 1849 | DWORD dwDependencyRegistrationAction = 0; | ||
| 1850 | DWORD64 qwEstimatedSize = 0; | ||
| 1851 | |||
| 1852 | // Deserialize message data. | ||
| 1853 | hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath); | ||
| 1854 | ExitOnFailure(hr, "Failed to read engine working path."); | ||
| 1855 | |||
| 1856 | hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); | ||
| 1857 | ExitOnFailure(hr, "Failed to read resume command line."); | ||
| 1858 | |||
| 1859 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); | ||
| 1860 | ExitOnFailure(hr, "Failed to read resume flag."); | ||
| 1861 | |||
| 1862 | hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations); | ||
| 1863 | ExitOnFailure(hr, "Failed to read registration operations."); | ||
| 1864 | |||
| 1865 | hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); | ||
| 1866 | ExitOnFailure(hr, "Failed to read dependency registration action."); | ||
| 1867 | |||
| 1868 | hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize); | ||
| 1869 | ExitOnFailure(hr, "Failed to read estimated size."); | ||
| 1870 | |||
| 1871 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 1872 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 1873 | |||
| 1874 | // Begin session in per-machine process. | ||
| 1875 | hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, pUserExperience, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize); | ||
| 1876 | ExitOnFailure(hr, "Failed to begin registration session."); | ||
| 1877 | |||
| 1878 | LExit: | ||
| 1879 | ReleaseStr(sczEngineWorkingPath); | ||
| 1880 | |||
| 1881 | return hr; | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | static HRESULT OnSessionResume( | ||
| 1885 | __in BURN_REGISTRATION* pRegistration, | ||
| 1886 | __in BURN_VARIABLES* pVariables, | ||
| 1887 | __in BYTE* pbData, | ||
| 1888 | __in DWORD cbData | ||
| 1889 | ) | ||
| 1890 | { | ||
| 1891 | HRESULT hr = S_OK; | ||
| 1892 | SIZE_T iData = 0; | ||
| 1893 | |||
| 1894 | // Deserialize message data. | ||
| 1895 | hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine); | ||
| 1896 | ExitOnFailure(hr, "Failed to read resume command line."); | ||
| 1897 | |||
| 1898 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume); | ||
| 1899 | ExitOnFailure(hr, "Failed to read resume flag."); | ||
| 1900 | |||
| 1901 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 1902 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 1903 | |||
| 1904 | // resume session in per-machine process | ||
| 1905 | hr = RegistrationSessionResume(pRegistration, pVariables); | ||
| 1906 | ExitOnFailure(hr, "Failed to resume registration session."); | ||
| 1907 | |||
| 1908 | LExit: | ||
| 1909 | return hr; | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | static HRESULT OnSessionEnd( | ||
| 1913 | __in BURN_REGISTRATION* pRegistration, | ||
| 1914 | __in BYTE* pbData, | ||
| 1915 | __in DWORD cbData | ||
| 1916 | ) | ||
| 1917 | { | ||
| 1918 | HRESULT hr = S_OK; | ||
| 1919 | SIZE_T iData = 0; | ||
| 1920 | DWORD dwResumeMode = 0; | ||
| 1921 | DWORD dwRestart = 0; | ||
| 1922 | DWORD dwDependencyRegistrationAction = 0; | ||
| 1923 | |||
| 1924 | // Deserialize message data. | ||
| 1925 | hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode); | ||
| 1926 | ExitOnFailure(hr, "Failed to read resume mode enum."); | ||
| 1927 | |||
| 1928 | hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart); | ||
| 1929 | ExitOnFailure(hr, "Failed to read restart enum."); | ||
| 1930 | |||
| 1931 | hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction); | ||
| 1932 | ExitOnFailure(hr, "Failed to read dependency registration action."); | ||
| 1933 | |||
| 1934 | // suspend session in per-machine process | ||
| 1935 | hr = RegistrationSessionEnd(pRegistration, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction); | ||
| 1936 | ExitOnFailure(hr, "Failed to suspend registration session."); | ||
| 1937 | |||
| 1938 | LExit: | ||
| 1939 | return hr; | ||
| 1940 | } | ||
| 1941 | |||
| 1942 | static HRESULT OnSaveState( | ||
| 1943 | __in BURN_REGISTRATION* pRegistration, | ||
| 1944 | __in BYTE* pbData, | ||
| 1945 | __in DWORD cbData | ||
| 1946 | ) | ||
| 1947 | { | ||
| 1948 | HRESULT hr = S_OK; | ||
| 1949 | |||
| 1950 | // save state in per-machine process | ||
| 1951 | hr = RegistrationSaveState(pRegistration, pbData, cbData); | ||
| 1952 | ExitOnFailure(hr, "Failed to save state."); | ||
| 1953 | |||
| 1954 | LExit: | ||
| 1955 | return hr; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | static HRESULT OnLayoutBundle( | ||
| 1959 | __in_z LPCWSTR wzExecutableName, | ||
| 1960 | __in BYTE* pbData, | ||
| 1961 | __in DWORD cbData | ||
| 1962 | ) | ||
| 1963 | { | ||
| 1964 | HRESULT hr = S_OK; | ||
| 1965 | SIZE_T iData = 0; | ||
| 1966 | LPWSTR sczLayoutDirectory = NULL; | ||
| 1967 | LPWSTR sczUnverifiedPath = NULL; | ||
| 1968 | |||
| 1969 | // Deserialize message data. | ||
| 1970 | hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); | ||
| 1971 | ExitOnFailure(hr, "Failed to read layout directory."); | ||
| 1972 | |||
| 1973 | hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); | ||
| 1974 | ExitOnFailure(hr, "Failed to read unverified bundle path."); | ||
| 1975 | |||
| 1976 | // Layout the bundle. | ||
| 1977 | hr = CacheLayoutBundle(wzExecutableName, sczLayoutDirectory, sczUnverifiedPath); | ||
| 1978 | ExitOnFailure(hr, "Failed to layout bundle from: %ls", sczUnverifiedPath); | ||
| 1979 | |||
| 1980 | LExit: | ||
| 1981 | ReleaseStr(sczUnverifiedPath); | ||
| 1982 | ReleaseStr(sczLayoutDirectory); | ||
| 1983 | |||
| 1984 | return hr; | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | static HRESULT OnCacheOrLayoutContainerOrPayload( | ||
| 1988 | __in BURN_CONTAINERS* pContainers, | ||
| 1989 | __in BURN_PACKAGES* pPackages, | ||
| 1990 | __in BURN_PAYLOADS* pPayloads, | ||
| 1991 | __in BYTE* pbData, | ||
| 1992 | __in DWORD cbData | ||
| 1993 | ) | ||
| 1994 | { | ||
| 1995 | HRESULT hr = S_OK; | ||
| 1996 | SIZE_T iData = 0; | ||
| 1997 | LPWSTR scz = NULL; | ||
| 1998 | BURN_CONTAINER* pContainer = NULL; | ||
| 1999 | BURN_PACKAGE* pPackage = NULL; | ||
| 2000 | BURN_PAYLOAD* pPayload = NULL; | ||
| 2001 | LPWSTR sczLayoutDirectory = NULL; | ||
| 2002 | LPWSTR sczUnverifiedPath = NULL; | ||
| 2003 | BOOL fMove = FALSE; | ||
| 2004 | |||
| 2005 | // Deserialize message data. | ||
| 2006 | hr = BuffReadString(pbData, cbData, &iData, &scz); | ||
| 2007 | ExitOnFailure(hr, "Failed to read package id."); | ||
| 2008 | |||
| 2009 | if (scz && *scz) | ||
| 2010 | { | ||
| 2011 | hr = ContainerFindById(pContainers, scz, &pContainer); | ||
| 2012 | ExitOnFailure(hr, "Failed to find container: %ls", scz); | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | hr = BuffReadString(pbData, cbData, &iData, &scz); | ||
| 2016 | ExitOnFailure(hr, "Failed to read package id."); | ||
| 2017 | |||
| 2018 | if (scz && *scz) | ||
| 2019 | { | ||
| 2020 | hr = PackageFindById(pPackages, scz, &pPackage); | ||
| 2021 | ExitOnFailure(hr, "Failed to find package: %ls", scz); | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | hr = BuffReadString(pbData, cbData, &iData, &scz); | ||
| 2025 | ExitOnFailure(hr, "Failed to read payload id."); | ||
| 2026 | |||
| 2027 | if (scz && *scz) | ||
| 2028 | { | ||
| 2029 | hr = PayloadFindById(pPayloads, scz, &pPayload); | ||
| 2030 | ExitOnFailure(hr, "Failed to find payload: %ls", scz); | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory); | ||
| 2034 | ExitOnFailure(hr, "Failed to read layout directory."); | ||
| 2035 | |||
| 2036 | hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath); | ||
| 2037 | ExitOnFailure(hr, "Failed to read unverified path."); | ||
| 2038 | |||
| 2039 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove); | ||
| 2040 | ExitOnFailure(hr, "Failed to read move flag."); | ||
| 2041 | |||
| 2042 | // Layout payload. | ||
| 2043 | if (sczLayoutDirectory && *sczLayoutDirectory) | ||
| 2044 | { | ||
| 2045 | if (pContainer) | ||
| 2046 | { | ||
| 2047 | Assert(!pPackage); | ||
| 2048 | Assert(!pPayload); | ||
| 2049 | |||
| 2050 | hr = CacheLayoutContainer(pContainer, sczLayoutDirectory, sczUnverifiedPath, fMove); | ||
| 2051 | ExitOnFailure(hr, "Failed to layout container from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); | ||
| 2052 | } | ||
| 2053 | else | ||
| 2054 | { | ||
| 2055 | hr = CacheLayoutPayload(pPayload, sczLayoutDirectory, sczUnverifiedPath, fMove); | ||
| 2056 | ExitOnFailure(hr, "Failed to layout payload from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory); | ||
| 2057 | } | ||
| 2058 | } | ||
| 2059 | else if (pPackage) // complete payload. | ||
| 2060 | { | ||
| 2061 | Assert(!pContainer); | ||
| 2062 | |||
| 2063 | hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove); | ||
| 2064 | ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey); | ||
| 2065 | } | ||
| 2066 | else | ||
| 2067 | { | ||
| 2068 | hr = E_INVALIDARG; | ||
| 2069 | ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload."); | ||
| 2070 | } | ||
| 2071 | |||
| 2072 | LExit: | ||
| 2073 | ReleaseStr(sczUnverifiedPath); | ||
| 2074 | ReleaseStr(sczLayoutDirectory); | ||
| 2075 | ReleaseStr(scz); | ||
| 2076 | |||
| 2077 | return hr; | ||
| 2078 | } | ||
| 2079 | |||
| 2080 | static void OnCacheCleanup( | ||
| 2081 | __in_z LPCWSTR wzBundleId | ||
| 2082 | ) | ||
| 2083 | { | ||
| 2084 | CacheCleanup(TRUE, wzBundleId); | ||
| 2085 | } | ||
| 2086 | |||
| 2087 | static HRESULT OnProcessDependentRegistration( | ||
| 2088 | __in const BURN_REGISTRATION* pRegistration, | ||
| 2089 | __in BYTE* pbData, | ||
| 2090 | __in DWORD cbData | ||
| 2091 | ) | ||
| 2092 | { | ||
| 2093 | HRESULT hr = S_OK; | ||
| 2094 | SIZE_T iData = 0; | ||
| 2095 | BURN_DEPENDENT_REGISTRATION_ACTION action = { }; | ||
| 2096 | |||
| 2097 | // Deserialize message data. | ||
| 2098 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type); | ||
| 2099 | ExitOnFailure(hr, "Failed to read action type."); | ||
| 2100 | |||
| 2101 | hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId); | ||
| 2102 | ExitOnFailure(hr, "Failed to read bundle id."); | ||
| 2103 | |||
| 2104 | hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey); | ||
| 2105 | ExitOnFailure(hr, "Failed to read dependent provider key."); | ||
| 2106 | |||
| 2107 | // Execute the registration action. | ||
| 2108 | hr = DependencyProcessDependentRegistration(pRegistration, &action); | ||
| 2109 | ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey); | ||
| 2110 | |||
| 2111 | LExit: | ||
| 2112 | // TODO: do the right thing here. | ||
| 2113 | //DependencyUninitializeRegistrationAction(&action); | ||
| 2114 | ReleaseStr(action.sczDependentProviderKey); | ||
| 2115 | ReleaseStr(action.sczBundleId) | ||
| 2116 | |||
| 2117 | return hr; | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | static HRESULT OnExecuteExePackage( | ||
| 2121 | __in HANDLE hPipe, | ||
| 2122 | __in BURN_PACKAGES* pPackages, | ||
| 2123 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 2124 | __in BURN_VARIABLES* pVariables, | ||
| 2125 | __in BYTE* pbData, | ||
| 2126 | __in DWORD cbData | ||
| 2127 | ) | ||
| 2128 | { | ||
| 2129 | HRESULT hr = S_OK; | ||
| 2130 | SIZE_T iData = 0; | ||
| 2131 | LPWSTR sczPackage = NULL; | ||
| 2132 | DWORD dwRollback = 0; | ||
| 2133 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2134 | LPWSTR sczIgnoreDependencies = NULL; | ||
| 2135 | LPWSTR sczAncestors = NULL; | ||
| 2136 | BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 2137 | |||
| 2138 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; | ||
| 2139 | |||
| 2140 | // Deserialize message data. | ||
| 2141 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2142 | ExitOnFailure(hr, "Failed to read EXE package id."); | ||
| 2143 | |||
| 2144 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action); | ||
| 2145 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2146 | |||
| 2147 | hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); | ||
| 2148 | ExitOnFailure(hr, "Failed to read rollback."); | ||
| 2149 | |||
| 2150 | hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies); | ||
| 2151 | ExitOnFailure(hr, "Failed to read the list of dependencies to ignore."); | ||
| 2152 | |||
| 2153 | hr = BuffReadString(pbData, cbData, &iData, &sczAncestors); | ||
| 2154 | ExitOnFailure(hr, "Failed to read the list of ancestors."); | ||
| 2155 | |||
| 2156 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 2157 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 2158 | |||
| 2159 | hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); | ||
| 2160 | if (E_NOTFOUND == hr) | ||
| 2161 | { | ||
| 2162 | hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage); | ||
| 2163 | } | ||
| 2164 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2165 | |||
| 2166 | // Pass the list of dependencies to ignore, if any, to the related bundle. | ||
| 2167 | if (sczIgnoreDependencies && *sczIgnoreDependencies) | ||
| 2168 | { | ||
| 2169 | hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0); | ||
| 2170 | ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | // Pass the list of ancestors, if any, to the related bundle. | ||
| 2174 | if (sczAncestors && *sczAncestors) | ||
| 2175 | { | ||
| 2176 | hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0); | ||
| 2177 | ExitOnFailure(hr, "Failed to allocate the list of ancestors."); | ||
| 2178 | } | ||
| 2179 | |||
| 2180 | // Execute EXE package. | ||
| 2181 | hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); | ||
| 2182 | ExitOnFailure(hr, "Failed to execute EXE package."); | ||
| 2183 | |||
| 2184 | LExit: | ||
| 2185 | ReleaseStr(sczAncestors); | ||
| 2186 | ReleaseStr(sczIgnoreDependencies); | ||
| 2187 | ReleaseStr(sczPackage); | ||
| 2188 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2189 | |||
| 2190 | if (SUCCEEDED(hr)) | ||
| 2191 | { | ||
| 2192 | if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart) | ||
| 2193 | { | ||
| 2194 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 2195 | } | ||
| 2196 | else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart) | ||
| 2197 | { | ||
| 2198 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); | ||
| 2199 | } | ||
| 2200 | } | ||
| 2201 | |||
| 2202 | return hr; | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | static HRESULT OnExecuteMsiPackage( | ||
| 2206 | __in HANDLE hPipe, | ||
| 2207 | __in BURN_PACKAGES* pPackages, | ||
| 2208 | __in BURN_VARIABLES* pVariables, | ||
| 2209 | __in BYTE* pbData, | ||
| 2210 | __in DWORD cbData | ||
| 2211 | ) | ||
| 2212 | { | ||
| 2213 | HRESULT hr = S_OK; | ||
| 2214 | SIZE_T iData = 0; | ||
| 2215 | LPWSTR sczPackage = NULL; | ||
| 2216 | HWND hwndParent = NULL; | ||
| 2217 | BOOL fRollback = 0; | ||
| 2218 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2219 | BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 2220 | |||
| 2221 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; | ||
| 2222 | |||
| 2223 | // Deserialize message data. | ||
| 2224 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2225 | ExitOnFailure(hr, "Failed to read MSI package id."); | ||
| 2226 | |||
| 2227 | hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage); | ||
| 2228 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2229 | |||
| 2230 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); | ||
| 2231 | ExitOnFailure(hr, "Failed to read parent hwnd."); | ||
| 2232 | |||
| 2233 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath); | ||
| 2234 | ExitOnFailure(hr, "Failed to read package log."); | ||
| 2235 | |||
| 2236 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel); | ||
| 2237 | ExitOnFailure(hr, "Failed to read UI level."); | ||
| 2238 | |||
| 2239 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action); | ||
| 2240 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2241 | |||
| 2242 | // Read feature actions. | ||
| 2243 | if (executeAction.msiPackage.pPackage->Msi.cFeatures) | ||
| 2244 | { | ||
| 2245 | executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE); | ||
| 2246 | ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); | ||
| 2247 | |||
| 2248 | for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i) | ||
| 2249 | { | ||
| 2250 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]); | ||
| 2251 | ExitOnFailure(hr, "Failed to read feature action."); | ||
| 2252 | } | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | // Read slipstream patches actions. | ||
| 2256 | if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages) | ||
| 2257 | { | ||
| 2258 | executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE); | ||
| 2259 | ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions."); | ||
| 2260 | |||
| 2261 | for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 2262 | { | ||
| 2263 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]); | ||
| 2264 | ExitOnFailure(hr, "Failed to read slipstream action."); | ||
| 2265 | } | ||
| 2266 | } | ||
| 2267 | |||
| 2268 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 2269 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 2270 | |||
| 2271 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); | ||
| 2272 | ExitOnFailure(hr, "Failed to read rollback flag."); | ||
| 2273 | |||
| 2274 | // Execute MSI package. | ||
| 2275 | hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); | ||
| 2276 | ExitOnFailure(hr, "Failed to execute MSI package."); | ||
| 2277 | |||
| 2278 | LExit: | ||
| 2279 | ReleaseStr(sczPackage); | ||
| 2280 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2281 | |||
| 2282 | if (SUCCEEDED(hr)) | ||
| 2283 | { | ||
| 2284 | if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart) | ||
| 2285 | { | ||
| 2286 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 2287 | } | ||
| 2288 | else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart) | ||
| 2289 | { | ||
| 2290 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); | ||
| 2291 | } | ||
| 2292 | } | ||
| 2293 | |||
| 2294 | return hr; | ||
| 2295 | } | ||
| 2296 | |||
| 2297 | static HRESULT OnExecuteMspPackage( | ||
| 2298 | __in HANDLE hPipe, | ||
| 2299 | __in BURN_PACKAGES* pPackages, | ||
| 2300 | __in BURN_VARIABLES* pVariables, | ||
| 2301 | __in BYTE* pbData, | ||
| 2302 | __in DWORD cbData | ||
| 2303 | ) | ||
| 2304 | { | ||
| 2305 | HRESULT hr = S_OK; | ||
| 2306 | SIZE_T iData = 0; | ||
| 2307 | LPWSTR sczPackage = NULL; | ||
| 2308 | HWND hwndParent = NULL; | ||
| 2309 | BOOL fRollback = 0; | ||
| 2310 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2311 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 2312 | |||
| 2313 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; | ||
| 2314 | |||
| 2315 | // Deserialize message data. | ||
| 2316 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2317 | ExitOnFailure(hr, "Failed to read MSP package id."); | ||
| 2318 | |||
| 2319 | hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage); | ||
| 2320 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2321 | |||
| 2322 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent); | ||
| 2323 | ExitOnFailure(hr, "Failed to read parent hwnd."); | ||
| 2324 | |||
| 2325 | executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product. | ||
| 2326 | |||
| 2327 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode); | ||
| 2328 | ExitOnFailure(hr, "Failed to read target product code."); | ||
| 2329 | |||
| 2330 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath); | ||
| 2331 | ExitOnFailure(hr, "Failed to read package log."); | ||
| 2332 | |||
| 2333 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel); | ||
| 2334 | ExitOnFailure(hr, "Failed to read UI level."); | ||
| 2335 | |||
| 2336 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action); | ||
| 2337 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2338 | |||
| 2339 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches); | ||
| 2340 | ExitOnFailure(hr, "Failed to read count of ordered patches."); | ||
| 2341 | |||
| 2342 | if (executeAction.mspTarget.cOrderedPatches) | ||
| 2343 | { | ||
| 2344 | executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE); | ||
| 2345 | ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches."); | ||
| 2346 | |||
| 2347 | for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i) | ||
| 2348 | { | ||
| 2349 | hr = BuffReadNumber(pbData, cbData, &iData, &executeAction.mspTarget.rgOrderedPatches[i].dwOrder); | ||
| 2350 | ExitOnFailure(hr, "Failed to read ordered patch order number."); | ||
| 2351 | |||
| 2352 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2353 | ExitOnFailure(hr, "Failed to read ordered patch package id."); | ||
| 2354 | |||
| 2355 | hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage); | ||
| 2356 | ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage); | ||
| 2357 | } | ||
| 2358 | } | ||
| 2359 | |||
| 2360 | hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); | ||
| 2361 | ExitOnFailure(hr, "Failed to read variables."); | ||
| 2362 | |||
| 2363 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); | ||
| 2364 | ExitOnFailure(hr, "Failed to read rollback flag."); | ||
| 2365 | |||
| 2366 | // Execute MSP package. | ||
| 2367 | hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart); | ||
| 2368 | ExitOnFailure(hr, "Failed to execute MSP package."); | ||
| 2369 | |||
| 2370 | LExit: | ||
| 2371 | ReleaseStr(sczPackage); | ||
| 2372 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2373 | |||
| 2374 | if (SUCCEEDED(hr)) | ||
| 2375 | { | ||
| 2376 | if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) | ||
| 2377 | { | ||
| 2378 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 2379 | } | ||
| 2380 | else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
| 2381 | { | ||
| 2382 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | return hr; | ||
| 2387 | } | ||
| 2388 | |||
| 2389 | static HRESULT OnExecuteMsuPackage( | ||
| 2390 | __in HANDLE hPipe, | ||
| 2391 | __in BURN_PACKAGES* pPackages, | ||
| 2392 | __in BURN_VARIABLES* pVariables, | ||
| 2393 | __in BYTE* pbData, | ||
| 2394 | __in DWORD cbData | ||
| 2395 | ) | ||
| 2396 | { | ||
| 2397 | HRESULT hr = S_OK; | ||
| 2398 | SIZE_T iData = 0; | ||
| 2399 | LPWSTR sczPackage = NULL; | ||
| 2400 | DWORD dwRollback = 0; | ||
| 2401 | DWORD dwStopWusaService = 0; | ||
| 2402 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2403 | BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 2404 | |||
| 2405 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; | ||
| 2406 | |||
| 2407 | // Deserialize message data. | ||
| 2408 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2409 | ExitOnFailure(hr, "Failed to read MSU package id."); | ||
| 2410 | |||
| 2411 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath); | ||
| 2412 | ExitOnFailure(hr, "Failed to read package log."); | ||
| 2413 | |||
| 2414 | hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.msuPackage.action)); | ||
| 2415 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2416 | |||
| 2417 | hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); | ||
| 2418 | ExitOnFailure(hr, "Failed to read rollback."); | ||
| 2419 | |||
| 2420 | hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService); | ||
| 2421 | ExitOnFailure(hr, "Failed to read StopWusaService."); | ||
| 2422 | |||
| 2423 | hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage); | ||
| 2424 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2425 | |||
| 2426 | // execute MSU package | ||
| 2427 | hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart); | ||
| 2428 | ExitOnFailure(hr, "Failed to execute MSU package."); | ||
| 2429 | |||
| 2430 | LExit: | ||
| 2431 | ReleaseStr(sczPackage); | ||
| 2432 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2433 | |||
| 2434 | if (SUCCEEDED(hr)) | ||
| 2435 | { | ||
| 2436 | if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart) | ||
| 2437 | { | ||
| 2438 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 2439 | } | ||
| 2440 | else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart) | ||
| 2441 | { | ||
| 2442 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED); | ||
| 2443 | } | ||
| 2444 | } | ||
| 2445 | |||
| 2446 | return hr; | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | static HRESULT OnExecutePackageProviderAction( | ||
| 2450 | __in BURN_PACKAGES* pPackages, | ||
| 2451 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 2452 | __in BYTE* pbData, | ||
| 2453 | __in DWORD cbData | ||
| 2454 | ) | ||
| 2455 | { | ||
| 2456 | HRESULT hr = S_OK; | ||
| 2457 | SIZE_T iData = 0; | ||
| 2458 | LPWSTR sczPackage = NULL; | ||
| 2459 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2460 | |||
| 2461 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER; | ||
| 2462 | |||
| 2463 | // Deserialize the message data. | ||
| 2464 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2465 | ExitOnFailure(hr, "Failed to read package id from message buffer."); | ||
| 2466 | |||
| 2467 | hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageProvider.action)); | ||
| 2468 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2469 | |||
| 2470 | // Find the package again. | ||
| 2471 | hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage); | ||
| 2472 | if (E_NOTFOUND == hr) | ||
| 2473 | { | ||
| 2474 | hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage); | ||
| 2475 | } | ||
| 2476 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2477 | |||
| 2478 | // Execute the package provider action. | ||
| 2479 | hr = DependencyExecutePackageProviderAction(&executeAction); | ||
| 2480 | ExitOnFailure(hr, "Failed to execute package provider action."); | ||
| 2481 | |||
| 2482 | LExit: | ||
| 2483 | ReleaseStr(sczPackage); | ||
| 2484 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2485 | |||
| 2486 | return hr; | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | static HRESULT OnExecutePackageDependencyAction( | ||
| 2490 | __in BURN_PACKAGES* pPackages, | ||
| 2491 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 2492 | __in BYTE* pbData, | ||
| 2493 | __in DWORD cbData | ||
| 2494 | ) | ||
| 2495 | { | ||
| 2496 | HRESULT hr = S_OK; | ||
| 2497 | SIZE_T iData = 0; | ||
| 2498 | LPWSTR sczPackage = NULL; | ||
| 2499 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2500 | |||
| 2501 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY; | ||
| 2502 | |||
| 2503 | // Deserialize the message data. | ||
| 2504 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2505 | ExitOnFailure(hr, "Failed to read package id from message buffer."); | ||
| 2506 | |||
| 2507 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey); | ||
| 2508 | ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer."); | ||
| 2509 | |||
| 2510 | hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageDependency.action)); | ||
| 2511 | ExitOnFailure(hr, "Failed to read action."); | ||
| 2512 | |||
| 2513 | // Find the package again. | ||
| 2514 | hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage); | ||
| 2515 | if (E_NOTFOUND == hr) | ||
| 2516 | { | ||
| 2517 | hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage); | ||
| 2518 | } | ||
| 2519 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2520 | |||
| 2521 | // Execute the package dependency action. | ||
| 2522 | hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction); | ||
| 2523 | ExitOnFailure(hr, "Failed to execute package dependency action."); | ||
| 2524 | |||
| 2525 | LExit: | ||
| 2526 | ReleaseStr(sczPackage); | ||
| 2527 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2528 | |||
| 2529 | return hr; | ||
| 2530 | } | ||
| 2531 | |||
| 2532 | static HRESULT OnLoadCompatiblePackage( | ||
| 2533 | __in BURN_PACKAGES* pPackages, | ||
| 2534 | __in BYTE* pbData, | ||
| 2535 | __in DWORD cbData | ||
| 2536 | ) | ||
| 2537 | { | ||
| 2538 | HRESULT hr = S_OK; | ||
| 2539 | SIZE_T iData = 0; | ||
| 2540 | LPWSTR sczPackage = NULL; | ||
| 2541 | BURN_EXECUTE_ACTION executeAction = { }; | ||
| 2542 | |||
| 2543 | executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; | ||
| 2544 | |||
| 2545 | // Deserialize the message data. | ||
| 2546 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2547 | ExitOnFailure(hr, "Failed to read package id from message buffer."); | ||
| 2548 | |||
| 2549 | // Find the reference package. | ||
| 2550 | hr = PackageFindById(pPackages, sczPackage, &executeAction.compatiblePackage.pReferencePackage); | ||
| 2551 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2552 | |||
| 2553 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode); | ||
| 2554 | ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer."); | ||
| 2555 | |||
| 2556 | hr = BuffReadNumber64(pbData, cbData, &iData, &executeAction.compatiblePackage.qwInstalledVersion); | ||
| 2557 | ExitOnFailure(hr, "Failed to read installed version from message buffer."); | ||
| 2558 | |||
| 2559 | // Copy the installed data to the reference package. | ||
| 2560 | hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0); | ||
| 2561 | ExitOnFailure(hr, "Failed to copy installed ProductCode."); | ||
| 2562 | |||
| 2563 | executeAction.compatiblePackage.pReferencePackage->Msi.qwInstalledVersion = executeAction.compatiblePackage.qwInstalledVersion; | ||
| 2564 | |||
| 2565 | // Load the compatible package and add it to the list. | ||
| 2566 | hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL); | ||
| 2567 | ExitOnFailure(hr, "Failed to load compatible package."); | ||
| 2568 | |||
| 2569 | LExit: | ||
| 2570 | ReleaseStr(sczPackage); | ||
| 2571 | PlanUninitializeExecuteAction(&executeAction); | ||
| 2572 | |||
| 2573 | return hr; | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | static int GenericExecuteMessageHandler( | ||
| 2577 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
| 2578 | __in LPVOID pvContext | ||
| 2579 | ) | ||
| 2580 | { | ||
| 2581 | HRESULT hr = S_OK; | ||
| 2582 | int nResult = IDOK; | ||
| 2583 | HANDLE hPipe = (HANDLE)pvContext; | ||
| 2584 | BYTE* pbData = NULL; | ||
| 2585 | SIZE_T cbData = 0; | ||
| 2586 | DWORD dwMessage = 0; | ||
| 2587 | |||
| 2588 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); | ||
| 2589 | ExitOnFailure(hr, "Failed to write UI flags."); | ||
| 2590 | |||
| 2591 | switch(pMessage->type) | ||
| 2592 | { | ||
| 2593 | case GENERIC_EXECUTE_MESSAGE_PROGRESS: | ||
| 2594 | // serialize message data | ||
| 2595 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); | ||
| 2596 | ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); | ||
| 2597 | |||
| 2598 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; | ||
| 2599 | break; | ||
| 2600 | |||
| 2601 | case GENERIC_EXECUTE_MESSAGE_ERROR: | ||
| 2602 | // serialize message data | ||
| 2603 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); | ||
| 2604 | ExitOnFailure(hr, "Failed to write error code to message buffer."); | ||
| 2605 | |||
| 2606 | hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); | ||
| 2607 | ExitOnFailure(hr, "Failed to write message to message buffer."); | ||
| 2608 | |||
| 2609 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; | ||
| 2610 | break; | ||
| 2611 | |||
| 2612 | case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE: | ||
| 2613 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles); | ||
| 2614 | ExitOnFailure(hr, "Failed to count of files in use to message buffer."); | ||
| 2615 | |||
| 2616 | for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i) | ||
| 2617 | { | ||
| 2618 | hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]); | ||
| 2619 | ExitOnFailure(hr, "Failed to write file in use to message buffer."); | ||
| 2620 | } | ||
| 2621 | |||
| 2622 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; | ||
| 2623 | break; | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | // send message | ||
| 2627 | hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast<DWORD*>(&nResult)); | ||
| 2628 | ExitOnFailure(hr, "Failed to send message to per-user process."); | ||
| 2629 | |||
| 2630 | LExit: | ||
| 2631 | ReleaseBuffer(pbData); | ||
| 2632 | |||
| 2633 | return nResult; | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | static int MsiExecuteMessageHandler( | ||
| 2637 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
| 2638 | __in_opt LPVOID pvContext | ||
| 2639 | ) | ||
| 2640 | { | ||
| 2641 | HRESULT hr = S_OK; | ||
| 2642 | int nResult = IDOK; | ||
| 2643 | HANDLE hPipe = (HANDLE)pvContext; | ||
| 2644 | BYTE* pbData = NULL; | ||
| 2645 | SIZE_T cbData = 0; | ||
| 2646 | DWORD dwMessage = 0; | ||
| 2647 | |||
| 2648 | // Always send any extra data via the struct first. | ||
| 2649 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData); | ||
| 2650 | ExitOnFailure(hr, "Failed to write MSI data count to message buffer."); | ||
| 2651 | |||
| 2652 | for (DWORD i = 0; i < pMessage->cData; ++i) | ||
| 2653 | { | ||
| 2654 | hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]); | ||
| 2655 | ExitOnFailure(hr, "Failed to write MSI data to message buffer."); | ||
| 2656 | } | ||
| 2657 | |||
| 2658 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults); | ||
| 2659 | ExitOnFailure(hr, "Failed to write UI flags."); | ||
| 2660 | |||
| 2661 | switch (pMessage->type) | ||
| 2662 | { | ||
| 2663 | case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: | ||
| 2664 | // serialize message data | ||
| 2665 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage); | ||
| 2666 | ExitOnFailure(hr, "Failed to write progress percentage to message buffer."); | ||
| 2667 | |||
| 2668 | // set message id | ||
| 2669 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS; | ||
| 2670 | break; | ||
| 2671 | |||
| 2672 | case WIU_MSI_EXECUTE_MESSAGE_ERROR: | ||
| 2673 | // serialize message data | ||
| 2674 | hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode); | ||
| 2675 | ExitOnFailure(hr, "Failed to write error code to message buffer."); | ||
| 2676 | |||
| 2677 | hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage); | ||
| 2678 | ExitOnFailure(hr, "Failed to write message to message buffer."); | ||
| 2679 | |||
| 2680 | // set message id | ||
| 2681 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR; | ||
| 2682 | break; | ||
| 2683 | |||
| 2684 | case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: | ||
| 2685 | // serialize message data | ||
| 2686 | hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt); | ||
| 2687 | ExitOnFailure(hr, "Failed to write MSI message type to message buffer."); | ||
| 2688 | |||
| 2689 | hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage); | ||
| 2690 | ExitOnFailure(hr, "Failed to write message to message buffer."); | ||
| 2691 | |||
| 2692 | // set message id | ||
| 2693 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE; | ||
| 2694 | break; | ||
| 2695 | |||
| 2696 | case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: | ||
| 2697 | // NOTE: we do not serialize other message data here because all the "files in use" are in the data above. | ||
| 2698 | |||
| 2699 | // set message id | ||
| 2700 | dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE; | ||
| 2701 | break; | ||
| 2702 | |||
| 2703 | default: | ||
| 2704 | hr = E_UNEXPECTED; | ||
| 2705 | ExitOnFailure(hr, "Invalid message type: %d", pMessage->type); | ||
| 2706 | } | ||
| 2707 | |||
| 2708 | // send message | ||
| 2709 | hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult); | ||
| 2710 | ExitOnFailure(hr, "Failed to send message to per-machine process."); | ||
| 2711 | |||
| 2712 | LExit: | ||
| 2713 | ReleaseBuffer(pbData); | ||
| 2714 | |||
| 2715 | return nResult; | ||
| 2716 | } | ||
| 2717 | |||
| 2718 | static HRESULT OnCleanPackage( | ||
| 2719 | __in BURN_PACKAGES* pPackages, | ||
| 2720 | __in BYTE* pbData, | ||
| 2721 | __in DWORD cbData | ||
| 2722 | ) | ||
| 2723 | { | ||
| 2724 | HRESULT hr = S_OK; | ||
| 2725 | SIZE_T iData = 0; | ||
| 2726 | LPWSTR sczPackage = NULL; | ||
| 2727 | BURN_PACKAGE* pPackage = NULL; | ||
| 2728 | |||
| 2729 | // Deserialize message data. | ||
| 2730 | hr = BuffReadString(pbData, cbData, &iData, &sczPackage); | ||
| 2731 | ExitOnFailure(hr, "Failed to read package id."); | ||
| 2732 | |||
| 2733 | hr = PackageFindById(pPackages, sczPackage, &pPackage); | ||
| 2734 | ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); | ||
| 2735 | |||
| 2736 | // Remove the package from the cache. | ||
| 2737 | hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId); | ||
| 2738 | ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId); | ||
| 2739 | |||
| 2740 | LExit: | ||
| 2741 | ReleaseStr(sczPackage); | ||
| 2742 | return hr; | ||
| 2743 | } | ||
| 2744 | |||
| 2745 | static HRESULT OnLaunchApprovedExe( | ||
| 2746 | __in HANDLE hPipe, | ||
| 2747 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 2748 | __in BURN_VARIABLES* pVariables, | ||
| 2749 | __in BYTE* pbData, | ||
| 2750 | __in DWORD cbData | ||
| 2751 | ) | ||
| 2752 | { | ||
| 2753 | HRESULT hr = S_OK; | ||
| 2754 | SIZE_T iData = 0; | ||
| 2755 | BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL; | ||
| 2756 | BURN_APPROVED_EXE* pApprovedExe = NULL; | ||
| 2757 | REGSAM samDesired = KEY_QUERY_VALUE; | ||
| 2758 | HKEY hKey = NULL; | ||
| 2759 | DWORD dwProcessId = 0; | ||
| 2760 | BYTE* pbSendData = NULL; | ||
| 2761 | SIZE_T cbSendData = 0; | ||
| 2762 | DWORD dwResult = 0; | ||
| 2763 | |||
| 2764 | pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE); | ||
| 2765 | |||
| 2766 | // Deserialize message data. | ||
| 2767 | hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId); | ||
| 2768 | ExitOnFailure(hr, "Failed to read approved exe id."); | ||
| 2769 | |||
| 2770 | hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments); | ||
| 2771 | ExitOnFailure(hr, "Failed to read approved exe arguments."); | ||
| 2772 | |||
| 2773 | hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout); | ||
| 2774 | ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout."); | ||
| 2775 | |||
| 2776 | hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe); | ||
| 2777 | ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId); | ||
| 2778 | |||
| 2779 | LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no"); | ||
| 2780 | |||
| 2781 | if (pApprovedExe->fWin64) | ||
| 2782 | { | ||
| 2783 | samDesired |= KEY_WOW64_64KEY; | ||
| 2784 | } | ||
| 2785 | |||
| 2786 | hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey); | ||
| 2787 | ExitOnFailure(hr, "Failed to open the registry key for the approved exe path."); | ||
| 2788 | |||
| 2789 | hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath); | ||
| 2790 | ExitOnFailure(hr, "Failed to read the value for the approved exe path."); | ||
| 2791 | |||
| 2792 | hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe); | ||
| 2793 | ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); | ||
| 2794 | if (S_FALSE == hr) | ||
| 2795 | { | ||
| 2796 | LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath); | ||
| 2797 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); | ||
| 2798 | } | ||
| 2799 | |||
| 2800 | hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId); | ||
| 2801 | ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath); | ||
| 2802 | |||
| 2803 | //send process id over pipe | ||
| 2804 | hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId); | ||
| 2805 | ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer."); | ||
| 2806 | |||
| 2807 | hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult); | ||
| 2808 | ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process."); | ||
| 2809 | |||
| 2810 | LExit: | ||
| 2811 | ReleaseBuffer(pbSendData); | ||
| 2812 | ApprovedExesUninitializeLaunch(pLaunchApprovedExe); | ||
| 2813 | return hr; | ||
| 2814 | } | ||
diff --git a/src/engine/elevation.h b/src/engine/elevation.h new file mode 100644 index 00000000..d82d9b1c --- /dev/null +++ b/src/engine/elevation.h | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // Parent (per-user process) side functions. | ||
| 11 | HRESULT ElevationElevate( | ||
| 12 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 13 | __in_opt HWND hwndParent | ||
| 14 | ); | ||
| 15 | HRESULT ElevationApplyInitialize( | ||
| 16 | __in HANDLE hPipe, | ||
| 17 | __in BURN_VARIABLES* pVariables, | ||
| 18 | __in BOOTSTRAPPER_ACTION action, | ||
| 19 | __in BURN_AU_PAUSE_ACTION auAction, | ||
| 20 | __in BOOL fTakeSystemRestorePoint | ||
| 21 | ); | ||
| 22 | HRESULT ElevationApplyUninitialize( | ||
| 23 | __in HANDLE hPipe | ||
| 24 | ); | ||
| 25 | HRESULT ElevationSessionBegin( | ||
| 26 | __in HANDLE hPipe, | ||
| 27 | __in_z LPCWSTR wzEngineWorkingPath, | ||
| 28 | __in_z LPCWSTR wzResumeCommandLine, | ||
| 29 | __in BOOL fDisableResume, | ||
| 30 | __in BURN_VARIABLES* pVariables, | ||
| 31 | __in DWORD dwRegistrationOperations, | ||
| 32 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, | ||
| 33 | __in DWORD64 qwEstimatedSize | ||
| 34 | ); | ||
| 35 | HRESULT ElevationSessionResume( | ||
| 36 | __in HANDLE hPipe, | ||
| 37 | __in_z LPCWSTR wzResumeCommandLine, | ||
| 38 | __in BOOL fDisableResume, | ||
| 39 | __in BURN_VARIABLES* pVariables | ||
| 40 | ); | ||
| 41 | HRESULT ElevationSessionEnd( | ||
| 42 | __in HANDLE hPipe, | ||
| 43 | __in BURN_RESUME_MODE resumeMode, | ||
| 44 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 45 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction | ||
| 46 | ); | ||
| 47 | HRESULT ElevationSaveState( | ||
| 48 | __in HANDLE hPipe, | ||
| 49 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 50 | __in SIZE_T cbBuffer | ||
| 51 | ); | ||
| 52 | HRESULT ElevationLayoutBundle( | ||
| 53 | __in HANDLE hPipe, | ||
| 54 | __in_z LPCWSTR wzLayoutDirectory, | ||
| 55 | __in_z LPCWSTR wzUnverifiedPath | ||
| 56 | ); | ||
| 57 | HRESULT ElevationCacheOrLayoutContainerOrPayload( | ||
| 58 | __in HANDLE hPipe, | ||
| 59 | __in_opt BURN_CONTAINER* pContainer, | ||
| 60 | __in_opt BURN_PACKAGE* pPackage, | ||
| 61 | __in_opt BURN_PAYLOAD* pPayload, | ||
| 62 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 63 | __in_z LPCWSTR wzUnverifiedPath, | ||
| 64 | __in BOOL fMove | ||
| 65 | ); | ||
| 66 | HRESULT ElevationCacheCleanup( | ||
| 67 | __in HANDLE hPipe | ||
| 68 | ); | ||
| 69 | HRESULT ElevationProcessDependentRegistration( | ||
| 70 | __in HANDLE hPipe, | ||
| 71 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 72 | ); | ||
| 73 | HRESULT ElevationExecuteExePackage( | ||
| 74 | __in HANDLE hPipe, | ||
| 75 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 76 | __in BURN_VARIABLES* pVariables, | ||
| 77 | __in BOOL fRollback, | ||
| 78 | __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, | ||
| 79 | __in LPVOID pvContext, | ||
| 80 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 81 | ); | ||
| 82 | HRESULT ElevationExecuteMsiPackage( | ||
| 83 | __in HANDLE hPipe, | ||
| 84 | __in_opt HWND hwndParent, | ||
| 85 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 86 | __in BURN_VARIABLES* pVariables, | ||
| 87 | __in BOOL fRollback, | ||
| 88 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 89 | __in LPVOID pvContext, | ||
| 90 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 91 | ); | ||
| 92 | HRESULT ElevationExecuteMspPackage( | ||
| 93 | __in HANDLE hPipe, | ||
| 94 | __in_opt HWND hwndParent, | ||
| 95 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 96 | __in BURN_VARIABLES* pVariables, | ||
| 97 | __in BOOL fRollback, | ||
| 98 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 99 | __in LPVOID pvContext, | ||
| 100 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 101 | ); | ||
| 102 | HRESULT ElevationExecuteMsuPackage( | ||
| 103 | __in HANDLE hPipe, | ||
| 104 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 105 | __in BOOL fRollback, | ||
| 106 | __in BOOL fStopWusaService, | ||
| 107 | __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, | ||
| 108 | __in LPVOID pvContext, | ||
| 109 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 110 | ); | ||
| 111 | HRESULT ElevationExecutePackageProviderAction( | ||
| 112 | __in HANDLE hPipe, | ||
| 113 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 114 | ); | ||
| 115 | HRESULT ElevationExecutePackageDependencyAction( | ||
| 116 | __in HANDLE hPipe, | ||
| 117 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 118 | ); | ||
| 119 | HRESULT ElevationLoadCompatiblePackageAction( | ||
| 120 | __in HANDLE hPipe, | ||
| 121 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 122 | ); | ||
| 123 | HRESULT ElevationLaunchElevatedChild( | ||
| 124 | __in HANDLE hPipe, | ||
| 125 | __in BURN_PACKAGE* pPackage, | ||
| 126 | __in LPCWSTR wzPipeName, | ||
| 127 | __in LPCWSTR wzPipeToken, | ||
| 128 | __out DWORD* pdwChildPid | ||
| 129 | ); | ||
| 130 | HRESULT ElevationCleanPackage( | ||
| 131 | __in HANDLE hPipe, | ||
| 132 | __in BURN_PACKAGE* pPackage | ||
| 133 | ); | ||
| 134 | HRESULT ElevationLaunchApprovedExe( | ||
| 135 | __in HANDLE hPipe, | ||
| 136 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
| 137 | __out DWORD* pdwProcessId | ||
| 138 | ); | ||
| 139 | |||
| 140 | // Child (per-machine process) side functions. | ||
| 141 | HRESULT ElevationChildPumpMessages( | ||
| 142 | __in DWORD dwLoggingTlsId, | ||
| 143 | __in HANDLE hPipe, | ||
| 144 | __in HANDLE hCachePipe, | ||
| 145 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
| 146 | __in BURN_CONTAINERS* pContainers, | ||
| 147 | __in BURN_PACKAGES* pPackages, | ||
| 148 | __in BURN_PAYLOADS* pPayloads, | ||
| 149 | __in BURN_VARIABLES* pVariables, | ||
| 150 | __in BURN_REGISTRATION* pRegistration, | ||
| 151 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 152 | __out HANDLE* phLock, | ||
| 153 | __out BOOL* pfDisabledAutomaticUpdates, | ||
| 154 | __out DWORD* pdwChildExitCode, | ||
| 155 | __out BOOL* pfRestart | ||
| 156 | ); | ||
| 157 | HRESULT ElevationChildResumeAutomaticUpdates(); | ||
| 158 | |||
| 159 | |||
| 160 | HRESULT ElevationMsiBeginTransaction( | ||
| 161 | __in HANDLE hPipe, | ||
| 162 | __in_opt HWND hwndParent, | ||
| 163 | __in LPVOID pvContext | ||
| 164 | ); | ||
| 165 | HRESULT ElevationMsiCommitTransaction( | ||
| 166 | __in HANDLE hPipe, | ||
| 167 | __in_opt HWND hwndParent, | ||
| 168 | __in LPVOID pvContext | ||
| 169 | ); | ||
| 170 | HRESULT ElevationMsiRollbackTransaction( | ||
| 171 | __in HANDLE hPipe, | ||
| 172 | __in_opt HWND hwndParent, | ||
| 173 | __in LPVOID pvContext | ||
| 174 | ); | ||
| 175 | |||
| 176 | #ifdef __cplusplus | ||
| 177 | } | ||
| 178 | #endif | ||
diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp new file mode 100644 index 00000000..09666980 --- /dev/null +++ b/src/engine/embedded.cpp | |||
| @@ -0,0 +1,197 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // struct | ||
| 7 | |||
| 8 | struct BURN_EMBEDDED_CALLBACK_CONTEXT | ||
| 9 | { | ||
| 10 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
| 11 | LPVOID pvContext; | ||
| 12 | }; | ||
| 13 | |||
| 14 | // internal function declarations | ||
| 15 | |||
| 16 | static HRESULT ProcessEmbeddedMessages( | ||
| 17 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 18 | __in_opt LPVOID pvContext, | ||
| 19 | __out DWORD* pdwResult | ||
| 20 | ); | ||
| 21 | static HRESULT OnEmbeddedErrorMessage( | ||
| 22 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 23 | __in LPVOID pvContext, | ||
| 24 | __in_bcount(cbData) BYTE* pbData, | ||
| 25 | __in DWORD cbData, | ||
| 26 | __out DWORD* pdwResult | ||
| 27 | ); | ||
| 28 | static HRESULT OnEmbeddedProgress( | ||
| 29 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 30 | __in LPVOID pvContext, | ||
| 31 | __in_bcount(cbData) BYTE* pbData, | ||
| 32 | __in DWORD cbData, | ||
| 33 | __out DWORD* pdwResult | ||
| 34 | ); | ||
| 35 | |||
| 36 | // function definitions | ||
| 37 | |||
| 38 | /******************************************************************* | ||
| 39 | EmbeddedLaunchChildProcess - | ||
| 40 | |||
| 41 | *******************************************************************/ | ||
| 42 | extern "C" HRESULT EmbeddedRunBundle( | ||
| 43 | __in LPCWSTR wzExecutablePath, | ||
| 44 | __in LPCWSTR wzArguments, | ||
| 45 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 46 | __in LPVOID pvContext, | ||
| 47 | __out DWORD* pdwExitCode | ||
| 48 | ) | ||
| 49 | { | ||
| 50 | HRESULT hr = S_OK; | ||
| 51 | DWORD dwCurrentProcessId = ::GetCurrentProcessId(); | ||
| 52 | HANDLE hCreatedPipesEvent = NULL; | ||
| 53 | LPWSTR sczCommand = NULL; | ||
| 54 | STARTUPINFOW si = { }; | ||
| 55 | PROCESS_INFORMATION pi = { }; | ||
| 56 | BURN_PIPE_RESULT result = { }; | ||
| 57 | |||
| 58 | BURN_PIPE_CONNECTION connection = { }; | ||
| 59 | PipeConnectionInitialize(&connection); | ||
| 60 | |||
| 61 | BURN_EMBEDDED_CALLBACK_CONTEXT context = { }; | ||
| 62 | context.pfnGenericMessageHandler = pfnGenericMessageHandler; | ||
| 63 | context.pvContext = pvContext; | ||
| 64 | |||
| 65 | hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); | ||
| 66 | ExitOnFailure(hr, "Failed to create embedded pipe name and client token."); | ||
| 67 | |||
| 68 | hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent); | ||
| 69 | ExitOnFailure(hr, "Failed to create embedded pipe."); | ||
| 70 | |||
| 71 | hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId); | ||
| 72 | ExitOnFailure(hr, "Failed to allocate embedded command."); | ||
| 73 | |||
| 74 | if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) | ||
| 75 | { | ||
| 76 | ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath); | ||
| 77 | } | ||
| 78 | |||
| 79 | connection.dwProcessId = ::GetProcessId(pi.hProcess); | ||
| 80 | connection.hProcess = pi.hProcess; | ||
| 81 | pi.hProcess = NULL; | ||
| 82 | |||
| 83 | hr = PipeWaitForChildConnect(&connection); | ||
| 84 | ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe."); | ||
| 85 | |||
| 86 | hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result); | ||
| 87 | ExitOnFailure(hr, "Failed to process messages from embedded message."); | ||
| 88 | |||
| 89 | // Get the return code from the embedded process. | ||
| 90 | hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode); | ||
| 91 | ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath); | ||
| 92 | |||
| 93 | LExit: | ||
| 94 | ReleaseHandle(pi.hThread); | ||
| 95 | ReleaseHandle(pi.hProcess); | ||
| 96 | |||
| 97 | StrSecureZeroFreeString(sczCommand); | ||
| 98 | ReleaseHandle(hCreatedPipesEvent); | ||
| 99 | PipeConnectionUninitialize(&connection); | ||
| 100 | |||
| 101 | return hr; | ||
| 102 | } | ||
| 103 | |||
| 104 | |||
| 105 | // internal function definitions | ||
| 106 | |||
| 107 | static HRESULT ProcessEmbeddedMessages( | ||
| 108 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 109 | __in_opt LPVOID pvContext, | ||
| 110 | __out DWORD* pdwResult | ||
| 111 | ) | ||
| 112 | { | ||
| 113 | HRESULT hr = S_OK; | ||
| 114 | BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext); | ||
| 115 | DWORD dwResult = 0; | ||
| 116 | |||
| 117 | // Process the message. | ||
| 118 | switch (pMsg->dwMessage) | ||
| 119 | { | ||
| 120 | case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: | ||
| 121 | hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); | ||
| 122 | ExitOnFailure(hr, "Failed to process embedded error message."); | ||
| 123 | break; | ||
| 124 | |||
| 125 | case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: | ||
| 126 | hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); | ||
| 127 | ExitOnFailure(hr, "Failed to process embedded progress message."); | ||
| 128 | break; | ||
| 129 | |||
| 130 | default: | ||
| 131 | hr = E_INVALIDARG; | ||
| 132 | ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage); | ||
| 133 | } | ||
| 134 | |||
| 135 | *pdwResult = dwResult; | ||
| 136 | |||
| 137 | LExit: | ||
| 138 | return hr; | ||
| 139 | } | ||
| 140 | |||
| 141 | static HRESULT OnEmbeddedErrorMessage( | ||
| 142 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 143 | __in LPVOID pvContext, | ||
| 144 | __in_bcount(cbData) BYTE* pbData, | ||
| 145 | __in DWORD cbData, | ||
| 146 | __out DWORD* pdwResult | ||
| 147 | ) | ||
| 148 | { | ||
| 149 | HRESULT hr = S_OK; | ||
| 150 | DWORD iData = 0; | ||
| 151 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 152 | LPWSTR sczMessage = NULL; | ||
| 153 | |||
| 154 | message.type = GENERIC_EXECUTE_MESSAGE_ERROR; | ||
| 155 | |||
| 156 | hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode); | ||
| 157 | ExitOnFailure(hr, "Failed to read error code from buffer."); | ||
| 158 | |||
| 159 | hr = BuffReadString(pbData, cbData, &iData, &sczMessage); | ||
| 160 | ExitOnFailure(hr, "Failed to read error message from buffer."); | ||
| 161 | |||
| 162 | message.error.wzMessage = sczMessage; | ||
| 163 | |||
| 164 | hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults); | ||
| 165 | ExitOnFailure(hr, "Failed to read UI hint from buffer."); | ||
| 166 | |||
| 167 | *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); | ||
| 168 | |||
| 169 | LExit: | ||
| 170 | ReleaseStr(sczMessage); | ||
| 171 | |||
| 172 | return hr; | ||
| 173 | } | ||
| 174 | |||
| 175 | static HRESULT OnEmbeddedProgress( | ||
| 176 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 177 | __in LPVOID pvContext, | ||
| 178 | __in_bcount(cbData) BYTE* pbData, | ||
| 179 | __in DWORD cbData, | ||
| 180 | __out DWORD* pdwResult | ||
| 181 | ) | ||
| 182 | { | ||
| 183 | HRESULT hr = S_OK; | ||
| 184 | DWORD iData = 0; | ||
| 185 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 186 | |||
| 187 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 188 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 189 | |||
| 190 | hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage); | ||
| 191 | ExitOnFailure(hr, "Failed to read progress from buffer."); | ||
| 192 | |||
| 193 | *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext); | ||
| 194 | |||
| 195 | LExit: | ||
| 196 | return hr; | ||
| 197 | } | ||
diff --git a/src/engine/embedded.h b/src/engine/embedded.h new file mode 100644 index 00000000..08adeae0 --- /dev/null +++ b/src/engine/embedded.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | typedef enum _BURN_EMBEDDED_MESSAGE_TYPE | ||
| 10 | { | ||
| 11 | BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN, | ||
| 12 | BURN_EMBEDDED_MESSAGE_TYPE_ERROR, | ||
| 13 | BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, | ||
| 14 | } BURN_EMBEDDED_MESSAGE_TYPE; | ||
| 15 | |||
| 16 | |||
| 17 | HRESULT EmbeddedRunBundle( | ||
| 18 | __in LPCWSTR wzExecutablePath, | ||
| 19 | __in LPCWSTR wzArguments, | ||
| 20 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 21 | __in LPVOID pvContext, | ||
| 22 | __out DWORD* pdwExitCode | ||
| 23 | ); | ||
| 24 | |||
| 25 | #ifdef __cplusplus | ||
| 26 | } | ||
| 27 | #endif | ||
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 00000000..3c0f09c9 --- /dev/null +++ b/src/engine/engine.cpp | |||
| @@ -0,0 +1,889 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | const DWORD RESTART_RETRIES = 10; | ||
| 9 | |||
| 10 | // internal function declarations | ||
| 11 | |||
| 12 | static HRESULT InitializeEngineState( | ||
| 13 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 14 | __in HANDLE hEngineFile | ||
| 15 | ); | ||
| 16 | static void UninitializeEngineState( | ||
| 17 | __in BURN_ENGINE_STATE* pEngineState | ||
| 18 | ); | ||
| 19 | static HRESULT RunUntrusted( | ||
| 20 | __in LPCWSTR wzCommandLine, | ||
| 21 | __in BURN_ENGINE_STATE* pEngineState | ||
| 22 | ); | ||
| 23 | static HRESULT RunNormal( | ||
| 24 | __in HINSTANCE hInstance, | ||
| 25 | __in BURN_ENGINE_STATE* pEngineState | ||
| 26 | ); | ||
| 27 | static HRESULT RunElevated( | ||
| 28 | __in HINSTANCE hInstance, | ||
| 29 | __in LPCWSTR wzCommandLine, | ||
| 30 | __in BURN_ENGINE_STATE* pEngineState | ||
| 31 | ); | ||
| 32 | static HRESULT RunEmbedded( | ||
| 33 | __in HINSTANCE hInstance, | ||
| 34 | __in BURN_ENGINE_STATE* pEngineState | ||
| 35 | ); | ||
| 36 | static HRESULT RunRunOnce( | ||
| 37 | __in const BURN_REGISTRATION* pRegistration, | ||
| 38 | __in int nCmdShow | ||
| 39 | ); | ||
| 40 | static HRESULT RunApplication( | ||
| 41 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 42 | __out BOOL* pfReloadApp | ||
| 43 | ); | ||
| 44 | static HRESULT ProcessMessage( | ||
| 45 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 46 | __in const MSG* pmsg | ||
| 47 | ); | ||
| 48 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
| 49 | __in_z LPCSTR szString, | ||
| 50 | __in_opt LPVOID pvContext | ||
| 51 | ); | ||
| 52 | static HRESULT Restart(); | ||
| 53 | |||
| 54 | |||
| 55 | // function definitions | ||
| 56 | |||
| 57 | extern "C" BOOL EngineInCleanRoom( | ||
| 58 | __in_z_opt LPCWSTR wzCommandLine | ||
| 59 | ) | ||
| 60 | { | ||
| 61 | // Be very careful with the functions you call from here. | ||
| 62 | // This function will be called before ::SetDefaultDllDirectories() | ||
| 63 | // has been called so dependencies outside of kernel32.dll are | ||
| 64 | // very likely to introduce DLL hijacking opportunities. | ||
| 65 | |||
| 66 | static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); | ||
| 67 | |||
| 68 | // This check is wholly dependent on the clean room command line switch being | ||
| 69 | // present at the beginning of the command line. Since Burn is the only thing | ||
| 70 | // that should be setting this command line option, that is in our control. | ||
| 71 | BOOL fInCleanRoom = (wzCommandLine && | ||
| 72 | (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') && | ||
| 73 | CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) && | ||
| 74 | wzCommandLine[1 + cchCleanRoomSwitch] == L'=' | ||
| 75 | ); | ||
| 76 | |||
| 77 | return fInCleanRoom; | ||
| 78 | } | ||
| 79 | |||
| 80 | extern "C" HRESULT EngineRun( | ||
| 81 | __in HINSTANCE hInstance, | ||
| 82 | __in HANDLE hEngineFile, | ||
| 83 | __in_z_opt LPCWSTR wzCommandLine, | ||
| 84 | __in int nCmdShow, | ||
| 85 | __out DWORD* pdwExitCode | ||
| 86 | ) | ||
| 87 | { | ||
| 88 | HRESULT hr = S_OK; | ||
| 89 | BOOL fComInitialized = FALSE; | ||
| 90 | BOOL fLogInitialized = FALSE; | ||
| 91 | BOOL fCrypInitialized = FALSE; | ||
| 92 | BOOL fRegInitialized = FALSE; | ||
| 93 | BOOL fWiuInitialized = FALSE; | ||
| 94 | BOOL fXmlInitialized = FALSE; | ||
| 95 | OSVERSIONINFOEXW ovix = { }; | ||
| 96 | LPWSTR sczExePath = NULL; | ||
| 97 | BOOL fRunNormal = FALSE; | ||
| 98 | BOOL fRestart = FALSE; | ||
| 99 | |||
| 100 | BURN_ENGINE_STATE engineState = { }; | ||
| 101 | |||
| 102 | // Always initialize logging first | ||
| 103 | LogInitialize(::GetModuleHandleW(NULL)); | ||
| 104 | fLogInitialized = TRUE; | ||
| 105 | |||
| 106 | // Ensure that log contains approriate level of information | ||
| 107 | #ifdef _DEBUG | ||
| 108 | LogSetLevel(REPORT_DEBUG, FALSE); | ||
| 109 | #else | ||
| 110 | LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed | ||
| 111 | #endif | ||
| 112 | |||
| 113 | hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv); | ||
| 114 | ExitOnFailure(hr, "Failed to parse command line."); | ||
| 115 | |||
| 116 | hr = InitializeEngineState(&engineState, hEngineFile); | ||
| 117 | ExitOnFailure(hr, "Failed to initialize engine state."); | ||
| 118 | |||
| 119 | engineState.command.nCmdShow = nCmdShow; | ||
| 120 | |||
| 121 | // initialize platform layer | ||
| 122 | PlatformInitialize(); | ||
| 123 | |||
| 124 | // initialize COM | ||
| 125 | hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); | ||
| 126 | ExitOnFailure(hr, "Failed to initialize COM."); | ||
| 127 | fComInitialized = TRUE; | ||
| 128 | |||
| 129 | // Initialize dutil. | ||
| 130 | hr = CrypInitialize(); | ||
| 131 | ExitOnFailure(hr, "Failed to initialize Cryputil."); | ||
| 132 | fCrypInitialized = TRUE; | ||
| 133 | |||
| 134 | hr = RegInitialize(); | ||
| 135 | ExitOnFailure(hr, "Failed to initialize Regutil."); | ||
| 136 | fRegInitialized = TRUE; | ||
| 137 | |||
| 138 | hr = WiuInitialize(); | ||
| 139 | ExitOnFailure(hr, "Failed to initialize Wiutil."); | ||
| 140 | fWiuInitialized = TRUE; | ||
| 141 | |||
| 142 | hr = XmlInitialize(); | ||
| 143 | ExitOnFailure(hr, "Failed to initialize XML util."); | ||
| 144 | fXmlInitialized = TRUE; | ||
| 145 | |||
| 146 | ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); | ||
| 147 | if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) | ||
| 148 | { | ||
| 149 | ExitWithLastError(hr, "Failed to get OS info."); | ||
| 150 | } | ||
| 151 | |||
| 152 | PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. | ||
| 153 | LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath); | ||
| 154 | ReleaseNullStr(sczExePath); | ||
| 155 | |||
| 156 | // initialize core | ||
| 157 | hr = CoreInitialize(&engineState); | ||
| 158 | ExitOnFailure(hr, "Failed to initialize core."); | ||
| 159 | |||
| 160 | // Select run mode. | ||
| 161 | switch (engineState.mode) | ||
| 162 | { | ||
| 163 | case BURN_MODE_UNTRUSTED: | ||
| 164 | hr = RunUntrusted(wzCommandLine, &engineState); | ||
| 165 | ExitOnFailure(hr, "Failed to run untrusted mode."); | ||
| 166 | break; | ||
| 167 | |||
| 168 | case BURN_MODE_NORMAL: | ||
| 169 | fRunNormal = TRUE; | ||
| 170 | |||
| 171 | hr = RunNormal(hInstance, &engineState); | ||
| 172 | ExitOnFailure(hr, "Failed to run per-user mode."); | ||
| 173 | break; | ||
| 174 | |||
| 175 | case BURN_MODE_ELEVATED: | ||
| 176 | hr = RunElevated(hInstance, wzCommandLine, &engineState); | ||
| 177 | ExitOnFailure(hr, "Failed to run per-machine mode."); | ||
| 178 | break; | ||
| 179 | |||
| 180 | case BURN_MODE_EMBEDDED: | ||
| 181 | fRunNormal = TRUE; | ||
| 182 | |||
| 183 | hr = RunEmbedded(hInstance, &engineState); | ||
| 184 | ExitOnFailure(hr, "Failed to run embedded mode."); | ||
| 185 | break; | ||
| 186 | |||
| 187 | case BURN_MODE_RUNONCE: | ||
| 188 | hr = RunRunOnce(&engineState.registration, nCmdShow); | ||
| 189 | ExitOnFailure(hr, "Failed to run RunOnce mode."); | ||
| 190 | break; | ||
| 191 | |||
| 192 | default: | ||
| 193 | hr = E_UNEXPECTED; | ||
| 194 | ExitOnFailure(hr, "Invalid run mode."); | ||
| 195 | } | ||
| 196 | |||
| 197 | // set exit code and remember if we are supposed to restart. | ||
| 198 | *pdwExitCode = engineState.userExperience.dwExitCode; | ||
| 199 | fRestart = engineState.fRestart; | ||
| 200 | |||
| 201 | LExit: | ||
| 202 | ReleaseStr(sczExePath); | ||
| 203 | |||
| 204 | // If anything went wrong but the log was never open, try to open a "failure" log | ||
| 205 | // and that will dump anything captured in the log memory buffer to the log. | ||
| 206 | if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) | ||
| 207 | { | ||
| 208 | LoggingOpenFailed(); | ||
| 209 | } | ||
| 210 | |||
| 211 | UserExperienceRemove(&engineState.userExperience); | ||
| 212 | |||
| 213 | CacheRemoveWorkingFolder(engineState.registration.sczId); | ||
| 214 | CacheUninitialize(); | ||
| 215 | |||
| 216 | // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. | ||
| 217 | if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) | ||
| 218 | { | ||
| 219 | LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); | ||
| 220 | |||
| 221 | fRestart = FALSE; | ||
| 222 | hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); | ||
| 223 | } | ||
| 224 | |||
| 225 | UninitializeEngineState(&engineState); | ||
| 226 | |||
| 227 | if (fXmlInitialized) | ||
| 228 | { | ||
| 229 | XmlUninitialize(); | ||
| 230 | } | ||
| 231 | |||
| 232 | if (fWiuInitialized) | ||
| 233 | { | ||
| 234 | WiuUninitialize(); | ||
| 235 | } | ||
| 236 | |||
| 237 | if (fRegInitialized) | ||
| 238 | { | ||
| 239 | RegUninitialize(); | ||
| 240 | } | ||
| 241 | |||
| 242 | if (fCrypInitialized) | ||
| 243 | { | ||
| 244 | CrypUninitialize(); | ||
| 245 | } | ||
| 246 | |||
| 247 | if (fComInitialized) | ||
| 248 | { | ||
| 249 | ::CoUninitialize(); | ||
| 250 | } | ||
| 251 | |||
| 252 | if (fRunNormal) | ||
| 253 | { | ||
| 254 | LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); | ||
| 255 | |||
| 256 | if (fRestart) | ||
| 257 | { | ||
| 258 | LogId(REPORT_STANDARD, MSG_RESTARTING); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | if (fLogInitialized) | ||
| 263 | { | ||
| 264 | LogClose(FALSE); | ||
| 265 | } | ||
| 266 | |||
| 267 | if (fRestart) | ||
| 268 | { | ||
| 269 | Restart(); | ||
| 270 | } | ||
| 271 | |||
| 272 | if (fLogInitialized) | ||
| 273 | { | ||
| 274 | LogUninitialize(FALSE); | ||
| 275 | } | ||
| 276 | |||
| 277 | return hr; | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 281 | // internal function definitions | ||
| 282 | |||
| 283 | static HRESULT InitializeEngineState( | ||
| 284 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 285 | __in HANDLE hEngineFile | ||
| 286 | ) | ||
| 287 | { | ||
| 288 | HRESULT hr = S_OK; | ||
| 289 | LPCWSTR wzParam = NULL; | ||
| 290 | HANDLE hSectionFile = hEngineFile; | ||
| 291 | HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; | ||
| 292 | |||
| 293 | pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; | ||
| 294 | pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES; | ||
| 295 | ::InitializeCriticalSection(&pEngineState->csActive); | ||
| 296 | ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
| 297 | PipeConnectionInitialize(&pEngineState->companionConnection); | ||
| 298 | PipeConnectionInitialize(&pEngineState->embeddedConnection); | ||
| 299 | |||
| 300 | for (int i = 0; i < pEngineState->argc; ++i) | ||
| 301 | { | ||
| 302 | if (pEngineState->argv[i][0] == L'-') | ||
| 303 | { | ||
| 304 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) | ||
| 305 | { | ||
| 306 | wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; | ||
| 307 | if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) | ||
| 308 | { | ||
| 309 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); | ||
| 310 | } | ||
| 311 | |||
| 312 | hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSourceEngineFile)); | ||
| 313 | ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); | ||
| 314 | } | ||
| 315 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF))) | ||
| 316 | { | ||
| 317 | wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)]; | ||
| 318 | if (L'=' != wzParam[-1] || L'\0' == wzParam[0]) | ||
| 319 | { | ||
| 320 | ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); | ||
| 321 | } | ||
| 322 | |||
| 323 | hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSectionFile)); | ||
| 324 | ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam)); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile); | ||
| 330 | ExitOnFailure(hr, "Failed to initialize engine section."); | ||
| 331 | |||
| 332 | LExit: | ||
| 333 | return hr; | ||
| 334 | } | ||
| 335 | |||
| 336 | static void UninitializeEngineState( | ||
| 337 | __in BURN_ENGINE_STATE* pEngineState | ||
| 338 | ) | ||
| 339 | { | ||
| 340 | if (pEngineState->argv) | ||
| 341 | { | ||
| 342 | AppFreeCommandLineArgs(pEngineState->argv); | ||
| 343 | } | ||
| 344 | |||
| 345 | ReleaseStr(pEngineState->sczIgnoreDependencies); | ||
| 346 | |||
| 347 | PipeConnectionUninitialize(&pEngineState->embeddedConnection); | ||
| 348 | PipeConnectionUninitialize(&pEngineState->companionConnection); | ||
| 349 | ReleaseStr(pEngineState->sczBundleEngineWorkingPath) | ||
| 350 | |||
| 351 | ReleaseHandle(pEngineState->hMessageWindowThread); | ||
| 352 | |||
| 353 | ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive); | ||
| 354 | UserExperienceUninitialize(&pEngineState->userExperience); | ||
| 355 | |||
| 356 | ApprovedExesUninitialize(&pEngineState->approvedExes); | ||
| 357 | UpdateUninitialize(&pEngineState->update); | ||
| 358 | VariablesUninitialize(&pEngineState->variables); | ||
| 359 | SearchesUninitialize(&pEngineState->searches); | ||
| 360 | RegistrationUninitialize(&pEngineState->registration); | ||
| 361 | PayloadsUninitialize(&pEngineState->payloads); | ||
| 362 | PackagesUninitialize(&pEngineState->packages); | ||
| 363 | CatalogUninitialize(&pEngineState->catalogs); | ||
| 364 | SectionUninitialize(&pEngineState->section); | ||
| 365 | ContainersUninitialize(&pEngineState->containers); | ||
| 366 | |||
| 367 | ReleaseStr(pEngineState->command.wzLayoutDirectory); | ||
| 368 | ReleaseStr(pEngineState->command.wzCommandLine); | ||
| 369 | |||
| 370 | ReleaseStr(pEngineState->log.sczExtension); | ||
| 371 | ReleaseStr(pEngineState->log.sczPrefix); | ||
| 372 | ReleaseStr(pEngineState->log.sczPath); | ||
| 373 | ReleaseStr(pEngineState->log.sczPathVariable); | ||
| 374 | |||
| 375 | if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId) | ||
| 376 | { | ||
| 377 | ::TlsFree(pEngineState->dwElevatedLoggingTlsId); | ||
| 378 | } | ||
| 379 | |||
| 380 | ::DeleteCriticalSection(&pEngineState->csActive); | ||
| 381 | |||
| 382 | // clear struct | ||
| 383 | memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); | ||
| 384 | } | ||
| 385 | |||
| 386 | static HRESULT RunUntrusted( | ||
| 387 | __in LPCWSTR wzCommandLine, | ||
| 388 | __in BURN_ENGINE_STATE* pEngineState | ||
| 389 | ) | ||
| 390 | { | ||
| 391 | HRESULT hr = S_OK; | ||
| 392 | LPWSTR sczCurrentProcessPath = NULL; | ||
| 393 | LPWSTR wzCleanRoomBundlePath = NULL; | ||
| 394 | LPWSTR sczCachedCleanRoomBundlePath = NULL; | ||
| 395 | LPWSTR sczParameters = NULL; | ||
| 396 | LPWSTR sczFullCommandLine = NULL; | ||
| 397 | STARTUPINFOW si = { }; | ||
| 398 | PROCESS_INFORMATION pi = { }; | ||
| 399 | HANDLE hFileAttached = NULL; | ||
| 400 | HANDLE hFileSelf = NULL; | ||
| 401 | HANDLE hProcess = NULL; | ||
| 402 | |||
| 403 | hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL); | ||
| 404 | ExitOnFailure(hr, "Failed to get path for current process."); | ||
| 405 | |||
| 406 | BOOL fRunningFromCache = CacheBundleRunningFromCache(); | ||
| 407 | |||
| 408 | // If we're running from the package cache, we're in a secure | ||
| 409 | // folder (DLLs cannot be inserted here for hijacking purposes) | ||
| 410 | // so just launch the current process's path as the clean room | ||
| 411 | // process. Technically speaking, we'd be able to skip creating | ||
| 412 | // a clean room process at all (since we're already running from | ||
| 413 | // a secure folder) but it makes the code that only wants to run | ||
| 414 | // in clean room more complicated if we don't launch an explicit | ||
| 415 | // clean room process. | ||
| 416 | if (fRunningFromCache) | ||
| 417 | { | ||
| 418 | wzCleanRoomBundlePath = sczCurrentProcessPath; | ||
| 419 | } | ||
| 420 | else | ||
| 421 | { | ||
| 422 | hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath); | ||
| 423 | ExitOnFailure(hr, "Failed to cache to clean room."); | ||
| 424 | |||
| 425 | wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath; | ||
| 426 | } | ||
| 427 | |||
| 428 | // The clean room switch must always be at the front of the command line so | ||
| 429 | // the EngineInCleanRoom function will operate correctly. | ||
| 430 | hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath); | ||
| 431 | ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); | ||
| 432 | |||
| 433 | // Send a file handle for the child Burn process to access the attached container. | ||
| 434 | hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters); | ||
| 435 | ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED); | ||
| 436 | |||
| 437 | // Grab a file handle for the child Burn process. | ||
| 438 | hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL); | ||
| 439 | ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); | ||
| 440 | |||
| 441 | hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine); | ||
| 442 | ExitOnFailure(hr, "Failed to append original command line."); | ||
| 443 | |||
| 444 | #ifdef ENABLE_UNELEVATE | ||
| 445 | // TODO: Pass file handle to unelevated process if this ever gets reenabled. | ||
| 446 | if (!pEngineState->fDisableUnelevate) | ||
| 447 | { | ||
| 448 | // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated). | ||
| 449 | hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess); | ||
| 450 | } | ||
| 451 | #endif | ||
| 452 | |||
| 453 | if (!hProcess) | ||
| 454 | { | ||
| 455 | hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters); | ||
| 456 | ExitOnFailure(hr, "Failed to allocate full command-line."); | ||
| 457 | |||
| 458 | si.cb = sizeof(si); | ||
| 459 | si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow); | ||
| 460 | if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) | ||
| 461 | { | ||
| 462 | ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); | ||
| 463 | } | ||
| 464 | |||
| 465 | hProcess = pi.hProcess; | ||
| 466 | pi.hProcess = NULL; | ||
| 467 | } | ||
| 468 | |||
| 469 | hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode); | ||
| 470 | ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath); | ||
| 471 | |||
| 472 | LExit: | ||
| 473 | ReleaseHandle(pi.hThread); | ||
| 474 | ReleaseFileHandle(hFileSelf); | ||
| 475 | ReleaseFileHandle(hFileAttached); | ||
| 476 | ReleaseHandle(hProcess); | ||
| 477 | StrSecureZeroFreeString(sczFullCommandLine); | ||
| 478 | StrSecureZeroFreeString(sczParameters); | ||
| 479 | ReleaseStr(sczCachedCleanRoomBundlePath); | ||
| 480 | ReleaseStr(sczCurrentProcessPath); | ||
| 481 | |||
| 482 | return hr; | ||
| 483 | } | ||
| 484 | |||
| 485 | static HRESULT RunNormal( | ||
| 486 | __in HINSTANCE hInstance, | ||
| 487 | __in BURN_ENGINE_STATE* pEngineState | ||
| 488 | ) | ||
| 489 | { | ||
| 490 | HRESULT hr = S_OK; | ||
| 491 | HANDLE hPipesCreatedEvent = NULL; | ||
| 492 | BOOL fContinueExecution = TRUE; | ||
| 493 | BOOL fReloadApp = FALSE; | ||
| 494 | |||
| 495 | // Initialize logging. | ||
| 496 | hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); | ||
| 497 | ExitOnFailure(hr, "Failed to open log."); | ||
| 498 | |||
| 499 | // Ensure we're on a supported operating system. | ||
| 500 | hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); | ||
| 501 | ExitOnFailure(hr, "Failed to check global conditions"); | ||
| 502 | |||
| 503 | if (!fContinueExecution) | ||
| 504 | { | ||
| 505 | LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); | ||
| 506 | |||
| 507 | // If the block told us to abort, abort! | ||
| 508 | ExitFunction1(hr = S_OK); | ||
| 509 | } | ||
| 510 | |||
| 511 | if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) | ||
| 512 | { | ||
| 513 | SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); | ||
| 514 | } | ||
| 515 | |||
| 516 | // Create a top-level window to handle system messages. | ||
| 517 | hr = UiCreateMessageWindow(hInstance, pEngineState); | ||
| 518 | ExitOnFailure(hr, "Failed to create the message window."); | ||
| 519 | |||
| 520 | // Query registration state. | ||
| 521 | hr = CoreQueryRegistration(pEngineState); | ||
| 522 | ExitOnFailure(hr, "Failed to query registration."); | ||
| 523 | |||
| 524 | // Set some built-in variables before loading the BA. | ||
| 525 | hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); | ||
| 526 | ExitOnFailure(hr, "Failed to set action variables."); | ||
| 527 | |||
| 528 | hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); | ||
| 529 | ExitOnFailure(hr, "Failed to set registration variables."); | ||
| 530 | |||
| 531 | // If a layout directory was specified on the command-line, set it as a well-known variable. | ||
| 532 | if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) | ||
| 533 | { | ||
| 534 | hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); | ||
| 535 | ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); | ||
| 536 | } | ||
| 537 | |||
| 538 | do | ||
| 539 | { | ||
| 540 | fReloadApp = FALSE; | ||
| 541 | |||
| 542 | hr = RunApplication(pEngineState, &fReloadApp); | ||
| 543 | ExitOnFailure(hr, "Failed while running "); | ||
| 544 | } while (fReloadApp); | ||
| 545 | |||
| 546 | LExit: | ||
| 547 | // If the message window is still around, close it. | ||
| 548 | UiCloseMessageWindow(pEngineState); | ||
| 549 | |||
| 550 | VariablesDump(&pEngineState->variables); | ||
| 551 | |||
| 552 | // end per-machine process if running | ||
| 553 | if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) | ||
| 554 | { | ||
| 555 | PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); | ||
| 556 | } | ||
| 557 | |||
| 558 | // If the splash screen is still around, close it. | ||
| 559 | if (::IsWindow(pEngineState->command.hwndSplashScreen)) | ||
| 560 | { | ||
| 561 | ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); | ||
| 562 | } | ||
| 563 | |||
| 564 | ReleaseHandle(hPipesCreatedEvent); | ||
| 565 | |||
| 566 | return hr; | ||
| 567 | } | ||
| 568 | |||
| 569 | static HRESULT RunElevated( | ||
| 570 | __in HINSTANCE hInstance, | ||
| 571 | __in LPCWSTR /*wzCommandLine*/, | ||
| 572 | __in BURN_ENGINE_STATE* pEngineState | ||
| 573 | ) | ||
| 574 | { | ||
| 575 | HRESULT hr = S_OK; | ||
| 576 | HANDLE hLock = NULL; | ||
| 577 | BOOL fDisabledAutomaticUpdates = FALSE; | ||
| 578 | |||
| 579 | // connect to per-user process | ||
| 580 | hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); | ||
| 581 | ExitOnFailure(hr, "Failed to connect to unelevated process."); | ||
| 582 | |||
| 583 | // Set up the thread local storage to store the correct pipe to communicate logging then | ||
| 584 | // override logging to write over the pipe. | ||
| 585 | pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); | ||
| 586 | if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) | ||
| 587 | { | ||
| 588 | ExitWithLastError(hr, "Failed to allocate thread local storage for logging."); | ||
| 589 | } | ||
| 590 | |||
| 591 | if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) | ||
| 592 | { | ||
| 593 | ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); | ||
| 594 | } | ||
| 595 | |||
| 596 | LogRedirect(RedirectLoggingOverPipe, pEngineState); | ||
| 597 | |||
| 598 | // Create a top-level window to prevent shutting down the elevated process. | ||
| 599 | hr = UiCreateMessageWindow(hInstance, pEngineState); | ||
| 600 | ExitOnFailure(hr, "Failed to create the message window."); | ||
| 601 | |||
| 602 | SrpInitialize(TRUE); | ||
| 603 | |||
| 604 | // Pump messages from parent process. | ||
| 605 | hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart); | ||
| 606 | LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log. | ||
| 607 | ExitOnFailure(hr, "Failed to pump messages from parent process."); | ||
| 608 | |||
| 609 | LExit: | ||
| 610 | LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. | ||
| 611 | |||
| 612 | // If the message window is still around, close it. | ||
| 613 | UiCloseMessageWindow(pEngineState); | ||
| 614 | |||
| 615 | if (fDisabledAutomaticUpdates) | ||
| 616 | { | ||
| 617 | ElevationChildResumeAutomaticUpdates(); | ||
| 618 | } | ||
| 619 | |||
| 620 | if (hLock) | ||
| 621 | { | ||
| 622 | ::ReleaseMutex(hLock); | ||
| 623 | ::CloseHandle(hLock); | ||
| 624 | } | ||
| 625 | |||
| 626 | return hr; | ||
| 627 | } | ||
| 628 | |||
| 629 | static HRESULT RunEmbedded( | ||
| 630 | __in HINSTANCE hInstance, | ||
| 631 | __in BURN_ENGINE_STATE* pEngineState | ||
| 632 | ) | ||
| 633 | { | ||
| 634 | HRESULT hr = S_OK; | ||
| 635 | |||
| 636 | // Disable system restore since the parent bundle may have done it. | ||
| 637 | pEngineState->fDisableSystemRestore = TRUE; | ||
| 638 | |||
| 639 | // Connect to parent process. | ||
| 640 | hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE); | ||
| 641 | ExitOnFailure(hr, "Failed to connect to parent of embedded process."); | ||
| 642 | |||
| 643 | // Do not register the bundle to automatically restart if embedded. | ||
| 644 | if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display) | ||
| 645 | { | ||
| 646 | pEngineState->registration.fDisableResume = TRUE; | ||
| 647 | } | ||
| 648 | |||
| 649 | // Now run the application like normal. | ||
| 650 | hr = RunNormal(hInstance, pEngineState); | ||
| 651 | ExitOnFailure(hr, "Failed to run bootstrapper application embedded."); | ||
| 652 | |||
| 653 | LExit: | ||
| 654 | return hr; | ||
| 655 | } | ||
| 656 | |||
| 657 | static HRESULT RunRunOnce( | ||
| 658 | __in const BURN_REGISTRATION* pRegistration, | ||
| 659 | __in int nCmdShow | ||
| 660 | ) | ||
| 661 | { | ||
| 662 | HRESULT hr = S_OK; | ||
| 663 | LPWSTR sczNewCommandLine = NULL; | ||
| 664 | LPWSTR sczBurnPath = NULL; | ||
| 665 | HANDLE hProcess = NULL; | ||
| 666 | |||
| 667 | hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine); | ||
| 668 | ExitOnFailure(hr, "Unable to get resume command line from the registry"); | ||
| 669 | |||
| 670 | // and re-launch | ||
| 671 | hr = PathForCurrentProcess(&sczBurnPath, NULL); | ||
| 672 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 673 | |||
| 674 | hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess); | ||
| 675 | ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath); | ||
| 676 | |||
| 677 | LExit: | ||
| 678 | ReleaseHandle(hProcess); | ||
| 679 | ReleaseStr(sczNewCommandLine); | ||
| 680 | ReleaseStr(sczBurnPath); | ||
| 681 | |||
| 682 | return hr; | ||
| 683 | } | ||
| 684 | |||
| 685 | static HRESULT RunApplication( | ||
| 686 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 687 | __out BOOL* pfReloadApp | ||
| 688 | ) | ||
| 689 | { | ||
| 690 | HRESULT hr = S_OK; | ||
| 691 | BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { }; | ||
| 692 | BOOL fStartupCalled = FALSE; | ||
| 693 | BOOL fRet = FALSE; | ||
| 694 | MSG msg = { }; | ||
| 695 | BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE; | ||
| 696 | |||
| 697 | ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); | ||
| 698 | |||
| 699 | // Setup the bootstrapper engine. | ||
| 700 | engineContext.dwThreadId = ::GetCurrentThreadId(); | ||
| 701 | engineContext.pEngineState = pEngineState; | ||
| 702 | |||
| 703 | // Load the bootstrapper application. | ||
| 704 | hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command); | ||
| 705 | ExitOnFailure(hr, "Failed to load BA."); | ||
| 706 | |||
| 707 | fStartupCalled = TRUE; | ||
| 708 | hr = UserExperienceOnStartup(&pEngineState->userExperience); | ||
| 709 | ExitOnFailure(hr, "Failed to start bootstrapper application."); | ||
| 710 | |||
| 711 | // Enter the message pump. | ||
| 712 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
| 713 | { | ||
| 714 | if (-1 == fRet) | ||
| 715 | { | ||
| 716 | hr = E_UNEXPECTED; | ||
| 717 | ExitOnRootFailure(hr, "Unexpected return value from message pump."); | ||
| 718 | } | ||
| 719 | else | ||
| 720 | { | ||
| 721 | ProcessMessage(pEngineState, &msg); | ||
| 722 | } | ||
| 723 | } | ||
| 724 | |||
| 725 | // Get exit code. | ||
| 726 | pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; | ||
| 727 | |||
| 728 | LExit: | ||
| 729 | if (fStartupCalled) | ||
| 730 | { | ||
| 731 | UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction); | ||
| 732 | if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction) | ||
| 733 | { | ||
| 734 | LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); | ||
| 735 | pEngineState->fRestart = TRUE; | ||
| 736 | } | ||
| 737 | else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction) | ||
| 738 | { | ||
| 739 | LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); | ||
| 740 | *pfReloadApp = TRUE; | ||
| 741 | } | ||
| 742 | } | ||
| 743 | |||
| 744 | // Unload BA. | ||
| 745 | UserExperienceUnload(&pEngineState->userExperience); | ||
| 746 | |||
| 747 | return hr; | ||
| 748 | } | ||
| 749 | |||
| 750 | static HRESULT ProcessMessage( | ||
| 751 | __in BURN_ENGINE_STATE* pEngineState, | ||
| 752 | __in const MSG* pmsg | ||
| 753 | ) | ||
| 754 | { | ||
| 755 | HRESULT hr = S_OK; | ||
| 756 | |||
| 757 | switch (pmsg->message) | ||
| 758 | { | ||
| 759 | case WM_BURN_DETECT: | ||
| 760 | hr = CoreDetect(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 761 | break; | ||
| 762 | |||
| 763 | case WM_BURN_PLAN: | ||
| 764 | hr = CorePlan(pEngineState, static_cast<BOOTSTRAPPER_ACTION>(pmsg->lParam)); | ||
| 765 | break; | ||
| 766 | |||
| 767 | case WM_BURN_ELEVATE: | ||
| 768 | hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 769 | break; | ||
| 770 | |||
| 771 | case WM_BURN_APPLY: | ||
| 772 | hr = CoreApply(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | ||
| 773 | break; | ||
| 774 | |||
| 775 | case WM_BURN_LAUNCH_APPROVED_EXE: | ||
| 776 | hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast<BURN_LAUNCH_APPROVED_EXE*>(pmsg->lParam)); | ||
| 777 | break; | ||
| 778 | |||
| 779 | case WM_BURN_QUIT: | ||
| 780 | hr = CoreQuit(pEngineState, static_cast<int>(pmsg->wParam)); | ||
| 781 | break; | ||
| 782 | } | ||
| 783 | |||
| 784 | return hr; | ||
| 785 | } | ||
| 786 | |||
| 787 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
| 788 | __in_z LPCSTR szString, | ||
| 789 | __in_opt LPVOID pvContext | ||
| 790 | ) | ||
| 791 | { | ||
| 792 | static BOOL s_fCurrentlyLoggingToPipe = FALSE; | ||
| 793 | |||
| 794 | HRESULT hr = S_OK; | ||
| 795 | BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext); | ||
| 796 | BOOL fStartedLogging = FALSE; | ||
| 797 | HANDLE hPipe = INVALID_HANDLE_VALUE; | ||
| 798 | BYTE* pbData = NULL; | ||
| 799 | SIZE_T cbData = 0; | ||
| 800 | DWORD dwResult = 0; | ||
| 801 | |||
| 802 | // Prevent this function from being called recursively. | ||
| 803 | if (s_fCurrentlyLoggingToPipe) | ||
| 804 | { | ||
| 805 | ExitFunction(); | ||
| 806 | } | ||
| 807 | |||
| 808 | s_fCurrentlyLoggingToPipe = TRUE; | ||
| 809 | fStartedLogging = TRUE; | ||
| 810 | |||
| 811 | // Make sure the current thread set the pipe in TLS. | ||
| 812 | hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId); | ||
| 813 | if (!hPipe || INVALID_HANDLE_VALUE == hPipe) | ||
| 814 | { | ||
| 815 | hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); | ||
| 816 | ExitFunction(); | ||
| 817 | } | ||
| 818 | |||
| 819 | // Do not log or use ExitOnFailure() macro here because they will be discarded | ||
| 820 | // by the recursive block at the top of this function. | ||
| 821 | hr = BuffWriteStringAnsi(&pbData, &cbData, szString); | ||
| 822 | if (SUCCEEDED(hr)) | ||
| 823 | { | ||
| 824 | hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); | ||
| 825 | if (SUCCEEDED(hr)) | ||
| 826 | { | ||
| 827 | hr = (HRESULT)dwResult; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | LExit: | ||
| 832 | ReleaseBuffer(pbData); | ||
| 833 | |||
| 834 | // We started logging so remember to say we are no longer logging. | ||
| 835 | if (fStartedLogging) | ||
| 836 | { | ||
| 837 | s_fCurrentlyLoggingToPipe = FALSE; | ||
| 838 | } | ||
| 839 | |||
| 840 | return hr; | ||
| 841 | } | ||
| 842 | |||
| 843 | static HRESULT Restart() | ||
| 844 | { | ||
| 845 | HRESULT hr = S_OK; | ||
| 846 | HANDLE hProcessToken = NULL; | ||
| 847 | TOKEN_PRIVILEGES priv = { }; | ||
| 848 | DWORD dwRetries = 0; | ||
| 849 | |||
| 850 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) | ||
| 851 | { | ||
| 852 | ExitWithLastError(hr, "Failed to get process token."); | ||
| 853 | } | ||
| 854 | |||
| 855 | priv.PrivilegeCount = 1; | ||
| 856 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | ||
| 857 | if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) | ||
| 858 | { | ||
| 859 | ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); | ||
| 860 | } | ||
| 861 | |||
| 862 | if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) | ||
| 863 | { | ||
| 864 | ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); | ||
| 865 | } | ||
| 866 | |||
| 867 | do | ||
| 868 | { | ||
| 869 | hr = S_OK; | ||
| 870 | |||
| 871 | // Wait a second to let the companion process (assuming we did an elevated install) to get to the | ||
| 872 | // point where it too is thinking about restarting the computer. Only one will schedule the restart | ||
| 873 | // but both will have their log files closed and otherwise be ready to exit. | ||
| 874 | // | ||
| 875 | // On retry, we'll also wait a second to let the OS try to get to a place where the restart can | ||
| 876 | // be initiated. | ||
| 877 | ::Sleep(1000); | ||
| 878 | |||
| 879 | if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED)) | ||
| 880 | { | ||
| 881 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 882 | } | ||
| 883 | } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr)); | ||
| 884 | ExitOnRootFailure(hr, "Failed to schedule restart."); | ||
| 885 | |||
| 886 | LExit: | ||
| 887 | ReleaseHandle(hProcessToken); | ||
| 888 | return hr; | ||
| 889 | } | ||
diff --git a/src/engine/engine.mc b/src/engine/engine.mc new file mode 100644 index 00000000..fb2dd6e9 --- /dev/null +++ b/src/engine/engine.mc | |||
| @@ -0,0 +1,901 @@ | |||
| 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 | |||
| 4 | MessageIdTypedef=DWORD | ||
| 5 | |||
| 6 | LanguageNames=(English=0x409:MSG00409) | ||
| 7 | |||
| 8 | |||
| 9 | ; // message definitions | ||
| 10 | |||
| 11 | ; // MessageId=# | ||
| 12 | ; // Severity=Success | ||
| 13 | ; // SymbolicName=MSG_SUCCESS | ||
| 14 | ; // Language=English | ||
| 15 | ; // Success %1. | ||
| 16 | ; // . | ||
| 17 | ; | ||
| 18 | ; // MessageId=# | ||
| 19 | ; // Severity=Warning | ||
| 20 | ; // SymbolicName=MSG_WARNING | ||
| 21 | ; // Language=English | ||
| 22 | ; // Warning %1. | ||
| 23 | ; // . | ||
| 24 | ; | ||
| 25 | ; // MessageId=# | ||
| 26 | ; // Severity=Error | ||
| 27 | ; // SymbolicName=MSG_ERROR | ||
| 28 | ; // Language=English | ||
| 29 | ; // Error %1. | ||
| 30 | ; // . | ||
| 31 | |||
| 32 | MessageId=1 | ||
| 33 | Severity=Success | ||
| 34 | SymbolicName=MSG_BURN_INFO | ||
| 35 | Language=English | ||
| 36 | Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls! | ||
| 37 | . | ||
| 38 | |||
| 39 | MessageId=2 | ||
| 40 | Severity=Warning | ||
| 41 | SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH | ||
| 42 | Language=English | ||
| 43 | Unknown burn internal command-line switch encountered: '%1!ls!'. | ||
| 44 | . | ||
| 45 | |||
| 46 | MessageId=3 | ||
| 47 | Severity=Success | ||
| 48 | SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE | ||
| 49 | Language=English | ||
| 50 | This bundle is being run by a related bundle as type '%1!hs!'. | ||
| 51 | . | ||
| 52 | |||
| 53 | MessageId=4 | ||
| 54 | Severity=Success | ||
| 55 | SymbolicName=MSG_BA_REQUESTED_RESTART | ||
| 56 | Language=English | ||
| 57 | Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. | ||
| 58 | . | ||
| 59 | |||
| 60 | MessageId=5 | ||
| 61 | Severity=Warning | ||
| 62 | SymbolicName=MSG_RESTARTING | ||
| 63 | Language=English | ||
| 64 | Restarting computer... | ||
| 65 | ======================================= | ||
| 66 | . | ||
| 67 | |||
| 68 | MessageId=6 | ||
| 69 | Severity=Success | ||
| 70 | SymbolicName=MSG_BA_REQUESTED_RELOAD | ||
| 71 | Language=English | ||
| 72 | Bootstrapper application requested to be reloaded. | ||
| 73 | . | ||
| 74 | |||
| 75 | MessageId=7 | ||
| 76 | Severity=Success | ||
| 77 | SymbolicName=MSG_EXITING | ||
| 78 | Language=English | ||
| 79 | Exit code: 0x%1!x!, restarting: %2!hs! | ||
| 80 | . | ||
| 81 | |||
| 82 | MessageId=8 | ||
| 83 | Severity=Warning | ||
| 84 | SymbolicName=MSG_RESTART_ABORTED | ||
| 85 | Language=English | ||
| 86 | Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. | ||
| 87 | . | ||
| 88 | |||
| 89 | MessageId=9 | ||
| 90 | Severity=Success | ||
| 91 | SymbolicName=MSG_BURN_COMMAND_LINE | ||
| 92 | Language=English | ||
| 93 | Command Line: '%1!ls!' | ||
| 94 | . | ||
| 95 | |||
| 96 | MessageId=10 | ||
| 97 | Severity=Success | ||
| 98 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING | ||
| 99 | Language=English | ||
| 100 | Launching elevated engine process. | ||
| 101 | . | ||
| 102 | |||
| 103 | MessageId=11 | ||
| 104 | Severity=Success | ||
| 105 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS | ||
| 106 | Language=English | ||
| 107 | Launched elevated engine process. | ||
| 108 | . | ||
| 109 | |||
| 110 | MessageId=12 | ||
| 111 | Severity=Success | ||
| 112 | SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS | ||
| 113 | Language=English | ||
| 114 | Connected to elevated engine. | ||
| 115 | . | ||
| 116 | |||
| 117 | MessageId=51 | ||
| 118 | Severity=Error | ||
| 119 | SymbolicName=MSG_FAILED_PARSE_CONDITION | ||
| 120 | Language=English | ||
| 121 | Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! | ||
| 122 | . | ||
| 123 | |||
| 124 | MessageId=52 | ||
| 125 | Severity=Success | ||
| 126 | SymbolicName=MSG_CONDITION_RESULT | ||
| 127 | Language=English | ||
| 128 | Condition '%1!ls!' evaluates to %2!hs!. | ||
| 129 | . | ||
| 130 | |||
| 131 | MessageId=53 | ||
| 132 | Severity=Error | ||
| 133 | SymbolicName=MSG_FAILED_CONDITION_CHECK | ||
| 134 | Language=English | ||
| 135 | Bundle global condition check didn't succeed - aborting without loading application. | ||
| 136 | . | ||
| 137 | |||
| 138 | MessageId=54 | ||
| 139 | Severity=Error | ||
| 140 | SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT | ||
| 141 | Language=English | ||
| 142 | Failed to resolve source for file: %2!ls!, error: %1!ls!. | ||
| 143 | . | ||
| 144 | |||
| 145 | MessageId=55 | ||
| 146 | Severity=Warning | ||
| 147 | SymbolicName=MSG_CANNOT_LOAD_STATE_FILE | ||
| 148 | Language=English | ||
| 149 | Could not load or read state file: %2!ls!, error: 0x%1!x!. | ||
| 150 | . | ||
| 151 | |||
| 152 | MessageId=56 | ||
| 153 | Severity=Error | ||
| 154 | SymbolicName=MSG_USER_CANCELED | ||
| 155 | Language=English | ||
| 156 | Application canceled operation: %2!ls!, error: %1!ls! | ||
| 157 | . | ||
| 158 | |||
| 159 | MessageId=100 | ||
| 160 | Severity=Success | ||
| 161 | SymbolicName=MSG_DETECT_BEGIN | ||
| 162 | Language=English | ||
| 163 | Detect begin, %1!u! packages | ||
| 164 | . | ||
| 165 | |||
| 166 | MessageId=101 | ||
| 167 | Severity=Success | ||
| 168 | SymbolicName=MSG_DETECTED_PACKAGE | ||
| 169 | Language=English | ||
| 170 | Detected package: %1!ls!, state: %2!hs!, cached: %3!hs! | ||
| 171 | . | ||
| 172 | |||
| 173 | MessageId=102 | ||
| 174 | Severity=Success | ||
| 175 | SymbolicName=MSG_DETECTED_RELATED_BUNDLE | ||
| 176 | Language=English | ||
| 177 | Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs! | ||
| 178 | . | ||
| 179 | |||
| 180 | MessageId=103 | ||
| 181 | Severity=Success | ||
| 182 | SymbolicName=MSG_DETECTED_RELATED_PACKAGE | ||
| 183 | Language=English | ||
| 184 | Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs! | ||
| 185 | . | ||
| 186 | |||
| 187 | MessageId=104 | ||
| 188 | Severity=Success | ||
| 189 | SymbolicName=MSG_DETECTED_MSI_FEATURE | ||
| 190 | Language=English | ||
| 191 | Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! | ||
| 192 | . | ||
| 193 | |||
| 194 | MessageId=105 | ||
| 195 | Severity=Success | ||
| 196 | SymbolicName=MSG_DETECTED_MSP_TARGET | ||
| 197 | Language=English | ||
| 198 | Detected package: %1!ls! target: %2!ls!, state: %3!hs! | ||
| 199 | . | ||
| 200 | |||
| 201 | MessageId=106 | ||
| 202 | Severity=Success | ||
| 203 | SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY | ||
| 204 | Language=English | ||
| 205 | Calculating patch applicability for target product code: %1!ls!, context: %2!hs! | ||
| 206 | . | ||
| 207 | |||
| 208 | MessageId=107 | ||
| 209 | Severity=Success | ||
| 210 | SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE | ||
| 211 | Language=English | ||
| 212 | Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs! | ||
| 213 | . | ||
| 214 | |||
| 215 | MessageId=108 | ||
| 216 | Severity=Success | ||
| 217 | SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER | ||
| 218 | Language=English | ||
| 219 | Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls! | ||
| 220 | . | ||
| 221 | |||
| 222 | MessageId=120 | ||
| 223 | Severity=Warning | ||
| 224 | SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED | ||
| 225 | Language=English | ||
| 226 | Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x! | ||
| 227 | . | ||
| 228 | |||
| 229 | MessageId=121 | ||
| 230 | Severity=Warning | ||
| 231 | SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY | ||
| 232 | Language=English | ||
| 233 | Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! | ||
| 234 | . | ||
| 235 | |||
| 236 | MessageId=151 | ||
| 237 | Severity=Error | ||
| 238 | SymbolicName=MSG_FAILED_DETECT_PACKAGE | ||
| 239 | Language=English | ||
| 240 | Detect failed for package: %2!ls!, error: %1!ls! | ||
| 241 | . | ||
| 242 | |||
| 243 | MessageId=152 | ||
| 244 | Severity=Error | ||
| 245 | SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE | ||
| 246 | Language=English | ||
| 247 | Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! | ||
| 248 | . | ||
| 249 | |||
| 250 | MessageId=170 | ||
| 251 | Severity=Warning | ||
| 252 | SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION | ||
| 253 | Language=English | ||
| 254 | Detected bad configuration for product: %1!ls! | ||
| 255 | . | ||
| 256 | |||
| 257 | MessageId=199 | ||
| 258 | Severity=Success | ||
| 259 | SymbolicName=MSG_DETECT_COMPLETE | ||
| 260 | Language=English | ||
| 261 | Detect complete, result: 0x%1!x! | ||
| 262 | . | ||
| 263 | |||
| 264 | MessageId=200 | ||
| 265 | Severity=Success | ||
| 266 | SymbolicName=MSG_PLAN_BEGIN | ||
| 267 | Language=English | ||
| 268 | Plan begin, %1!u! packages, action: %2!hs! | ||
| 269 | . | ||
| 270 | |||
| 271 | MessageId=201 | ||
| 272 | Severity=Success | ||
| 273 | SymbolicName=MSG_PLANNED_PACKAGE | ||
| 274 | Language=English | ||
| 275 | Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs! | ||
| 276 | . | ||
| 277 | |||
| 278 | MessageId=202 | ||
| 279 | Severity=Success | ||
| 280 | SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST | ||
| 281 | Language=English | ||
| 282 | Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! | ||
| 283 | . | ||
| 284 | |||
| 285 | MessageId=203 | ||
| 286 | Severity=Success | ||
| 287 | SymbolicName=MSG_PLANNED_MSI_FEATURE | ||
| 288 | Language=English | ||
| 289 | Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! | ||
| 290 | . | ||
| 291 | |||
| 292 | MessageId=204 | ||
| 293 | Severity=Success | ||
| 294 | SymbolicName=MSG_PLAN_MSI_FEATURES | ||
| 295 | Language=English | ||
| 296 | Plan %1!u! msi features for package: %2!ls! | ||
| 297 | . | ||
| 298 | |||
| 299 | MessageId=205 | ||
| 300 | Severity=Warning | ||
| 301 | SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION | ||
| 302 | Language=English | ||
| 303 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled | ||
| 304 | . | ||
| 305 | |||
| 306 | MessageId=206 | ||
| 307 | Severity=Warning | ||
| 308 | SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION | ||
| 309 | Language=English | ||
| 310 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! | ||
| 311 | . | ||
| 312 | |||
| 313 | MessageId=207 | ||
| 314 | Severity=Success | ||
| 315 | SymbolicName=MSG_PLANNED_RELATED_BUNDLE | ||
| 316 | Language=English | ||
| 317 | Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! | ||
| 318 | . | ||
| 319 | |||
| 320 | MessageId=208 | ||
| 321 | Severity=Warning | ||
| 322 | SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE | ||
| 323 | Language=English | ||
| 324 | Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs! | ||
| 325 | . | ||
| 326 | |||
| 327 | MessageId=209 | ||
| 328 | Severity=Warning | ||
| 329 | SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL | ||
| 330 | Language=English | ||
| 331 | Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! | ||
| 332 | . | ||
| 333 | |||
| 334 | MessageId=210 | ||
| 335 | Severity=Warning | ||
| 336 | SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS | ||
| 337 | Language=English | ||
| 338 | Plan skipped due to %1!u! remaining dependents | ||
| 339 | . | ||
| 340 | |||
| 341 | MessageId=211 | ||
| 342 | Severity=Success | ||
| 343 | SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE | ||
| 344 | Language=English | ||
| 345 | Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
| 346 | . | ||
| 347 | |||
| 348 | MessageId=212 | ||
| 349 | Severity=Success | ||
| 350 | SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE | ||
| 351 | Language=English | ||
| 352 | Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
| 353 | . | ||
| 354 | |||
| 355 | MessageId=213 | ||
| 356 | Severity=Success | ||
| 357 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT | ||
| 358 | Language=English | ||
| 359 | Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!. | ||
| 360 | . | ||
| 361 | |||
| 362 | MessageId=214 | ||
| 363 | Severity=Success | ||
| 364 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED | ||
| 365 | Language=English | ||
| 366 | Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. | ||
| 367 | . | ||
| 368 | |||
| 369 | MessageId=215 | ||
| 370 | Severity=Success | ||
| 371 | SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER | ||
| 372 | Language=English | ||
| 373 | Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls! | ||
| 374 | . | ||
| 375 | |||
| 376 | MessageId=216 | ||
| 377 | Severity=Success | ||
| 378 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER | ||
| 379 | Language=English | ||
| 380 | Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed. | ||
| 381 | . | ||
| 382 | |||
| 383 | MessageId=217 | ||
| 384 | Severity=Success | ||
| 385 | SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR | ||
| 386 | Language=English | ||
| 387 | Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. | ||
| 388 | . | ||
| 389 | |||
| 390 | MessageId=299 | ||
| 391 | Severity=Success | ||
| 392 | SymbolicName=MSG_PLAN_COMPLETE | ||
| 393 | Language=English | ||
| 394 | Plan complete, result: 0x%1!x! | ||
| 395 | . | ||
| 396 | |||
| 397 | MessageId=300 | ||
| 398 | Severity=Success | ||
| 399 | SymbolicName=MSG_APPLY_BEGIN | ||
| 400 | Language=English | ||
| 401 | Apply begin | ||
| 402 | . | ||
| 403 | |||
| 404 | MessageId=301 | ||
| 405 | Severity=Success | ||
| 406 | SymbolicName=MSG_APPLYING_PACKAGE | ||
| 407 | Language=English | ||
| 408 | Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' | ||
| 409 | . | ||
| 410 | |||
| 411 | MessageId=302 | ||
| 412 | Severity=Success | ||
| 413 | SymbolicName=MSG_ACQUIRED_PAYLOAD | ||
| 414 | Language=English | ||
| 415 | Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. | ||
| 416 | . | ||
| 417 | |||
| 418 | MessageId=304 | ||
| 419 | Severity=Success | ||
| 420 | SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD | ||
| 421 | Language=English | ||
| 422 | Verified existing payload: %1!ls! at path: %2!ls!. | ||
| 423 | . | ||
| 424 | |||
| 425 | MessageId=305 | ||
| 426 | Severity=Success | ||
| 427 | SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD | ||
| 428 | Language=English | ||
| 429 | Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. | ||
| 430 | . | ||
| 431 | |||
| 432 | MessageId=306 | ||
| 433 | Severity=Success | ||
| 434 | SymbolicName=MSG_APPLYING_PATCH_PACKAGE | ||
| 435 | Language=English | ||
| 436 | Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' | ||
| 437 | . | ||
| 438 | |||
| 439 | MessageId=307 | ||
| 440 | Severity=Warning | ||
| 441 | SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE | ||
| 442 | Language=English | ||
| 443 | Attempted to uninstall absent package: %1!ls!. Continuing... | ||
| 444 | . | ||
| 445 | |||
| 446 | MessageId=308 | ||
| 447 | Severity=Warning | ||
| 448 | SymbolicName=MSG_FAILED_PAUSE_AU | ||
| 449 | Language=English | ||
| 450 | Automatic updates could not be paused due to error: 0x%1!x!. Continuing... | ||
| 451 | . | ||
| 452 | |||
| 453 | MessageId=309 | ||
| 454 | Severity=Warning | ||
| 455 | SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE | ||
| 456 | Language=English | ||
| 457 | Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... | ||
| 458 | . | ||
| 459 | |||
| 460 | MessageId=310 | ||
| 461 | Severity=Error | ||
| 462 | SymbolicName=MSG_FAILED_VERIFY_PAYLOAD | ||
| 463 | Language=English | ||
| 464 | Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. | ||
| 465 | . | ||
| 466 | |||
| 467 | MessageId=311 | ||
| 468 | Severity=Error | ||
| 469 | SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER | ||
| 470 | Language=English | ||
| 471 | Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
| 472 | . | ||
| 473 | |||
| 474 | MessageId=312 | ||
| 475 | Severity=Error | ||
| 476 | SymbolicName=MSG_FAILED_EXTRACT_CONTAINER | ||
| 477 | Language=English | ||
| 478 | Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
| 479 | . | ||
| 480 | |||
| 481 | MessageId=313 | ||
| 482 | Severity=Error | ||
| 483 | SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD | ||
| 484 | Language=English | ||
| 485 | Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
| 486 | . | ||
| 487 | |||
| 488 | MessageId=314 | ||
| 489 | Severity=Error | ||
| 490 | SymbolicName=MSG_FAILED_CACHE_PAYLOAD | ||
| 491 | Language=English | ||
| 492 | Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!. | ||
| 493 | . | ||
| 494 | |||
| 495 | MessageId=315 | ||
| 496 | Severity=Error | ||
| 497 | SymbolicName=MSG_FAILED_LAYOUT_BUNDLE | ||
| 498 | Language=English | ||
| 499 | Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
| 500 | . | ||
| 501 | |||
| 502 | MessageId=316 | ||
| 503 | Severity=Error | ||
| 504 | SymbolicName=MSG_FAILED_LAYOUT_CONTAINER | ||
| 505 | Language=English | ||
| 506 | Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
| 507 | . | ||
| 508 | |||
| 509 | |||
| 510 | MessageId=317 | ||
| 511 | Severity=Error | ||
| 512 | SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD | ||
| 513 | Language=English | ||
| 514 | Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
| 515 | . | ||
| 516 | |||
| 517 | MessageId=318 | ||
| 518 | Severity=Success | ||
| 519 | SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED | ||
| 520 | Language=English | ||
| 521 | Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! | ||
| 522 | . | ||
| 523 | |||
| 524 | MessageId=319 | ||
| 525 | Severity=Success | ||
| 526 | SymbolicName=MSG_APPLY_COMPLETED_PACKAGE | ||
| 527 | Language=English | ||
| 528 | Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! | ||
| 529 | . | ||
| 530 | |||
| 531 | MessageId=320 | ||
| 532 | Severity=Success | ||
| 533 | SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER | ||
| 534 | Language=English | ||
| 535 | Registering bundle dependency provider: %1!ls!, version: %2!ls! | ||
| 536 | . | ||
| 537 | |||
| 538 | MessageId=321 | ||
| 539 | Severity=Warning | ||
| 540 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS | ||
| 541 | Language=English | ||
| 542 | Skipping dependency registration on package with no dependency providers: %1!ls! | ||
| 543 | . | ||
| 544 | |||
| 545 | MessageId=322 | ||
| 546 | Severity=Warning | ||
| 547 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE | ||
| 548 | Language=English | ||
| 549 | Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! | ||
| 550 | . | ||
| 551 | |||
| 552 | MessageId=323 | ||
| 553 | Severity=Success | ||
| 554 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER | ||
| 555 | Language=English | ||
| 556 | Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! | ||
| 557 | . | ||
| 558 | |||
| 559 | MessageId=324 | ||
| 560 | Severity=Warning | ||
| 561 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING | ||
| 562 | Language=English | ||
| 563 | Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! | ||
| 564 | . | ||
| 565 | |||
| 566 | MessageId=325 | ||
| 567 | Severity=Success | ||
| 568 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY | ||
| 569 | Language=English | ||
| 570 | Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! | ||
| 571 | . | ||
| 572 | |||
| 573 | MessageId=326 | ||
| 574 | Severity=Success | ||
| 575 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY | ||
| 576 | Language=English | ||
| 577 | Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! | ||
| 578 | . | ||
| 579 | |||
| 580 | MessageId=327 | ||
| 581 | Severity=Warning | ||
| 582 | SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS | ||
| 583 | Language=English | ||
| 584 | Will not uninstall package: %1!ls!, found dependents: %2!d! | ||
| 585 | . | ||
| 586 | |||
| 587 | MessageId=328 | ||
| 588 | Severity=Warning | ||
| 589 | SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT | ||
| 590 | Language=English | ||
| 591 | Found dependent: %1!ls!, name: %2!ls! | ||
| 592 | . | ||
| 593 | |||
| 594 | MessageId=329 | ||
| 595 | Severity=Success | ||
| 596 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED | ||
| 597 | Language=English | ||
| 598 | Removed package dependency provider: %1!ls!, package: %2!ls! | ||
| 599 | . | ||
| 600 | |||
| 601 | MessageId=330 | ||
| 602 | Severity=Success | ||
| 603 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED | ||
| 604 | Language=English | ||
| 605 | Removed bundle dependency provider: %1!ls! | ||
| 606 | . | ||
| 607 | |||
| 608 | MessageId=331 | ||
| 609 | Severity=Warning | ||
| 610 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED | ||
| 611 | Language=English | ||
| 612 | Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! | ||
| 613 | . | ||
| 614 | |||
| 615 | MessageId=332 | ||
| 616 | Severity=Warning | ||
| 617 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED | ||
| 618 | Language=English | ||
| 619 | Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! | ||
| 620 | . | ||
| 621 | |||
| 622 | MessageId=333 | ||
| 623 | Severity=Warning | ||
| 624 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED | ||
| 625 | Language=English | ||
| 626 | Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! | ||
| 627 | . | ||
| 628 | |||
| 629 | MessageId=335 | ||
| 630 | Severity=Success | ||
| 631 | SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD | ||
| 632 | Language=English | ||
| 633 | Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! | ||
| 634 | . | ||
| 635 | |||
| 636 | MessageId=336 | ||
| 637 | Severity=Success | ||
| 638 | SymbolicName=MSG_ACQUIRE_CONTAINER | ||
| 639 | Language=English | ||
| 640 | Acquiring container: %1!ls!, %3!hs! from: %4!ls! | ||
| 641 | . | ||
| 642 | |||
| 643 | MessageId=337 | ||
| 644 | Severity=Success | ||
| 645 | SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD | ||
| 646 | Language=English | ||
| 647 | Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! | ||
| 648 | . | ||
| 649 | |||
| 650 | MessageId=338 | ||
| 651 | Severity=Success | ||
| 652 | SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD | ||
| 653 | Language=English | ||
| 654 | Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! | ||
| 655 | . | ||
| 656 | |||
| 657 | MessageId=340 | ||
| 658 | Severity=Warning | ||
| 659 | SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE | ||
| 660 | Language=English | ||
| 661 | Prompt for source of bundle payload: %2!ls!, path: %3!ls! | ||
| 662 | . | ||
| 663 | |||
| 664 | MessageId=341 | ||
| 665 | Severity=Warning | ||
| 666 | SymbolicName=MSG_PROMPT_CONTAINER_SOURCE | ||
| 667 | Language=English | ||
| 668 | Prompt for source of container: %1!ls!, path: %3!ls! | ||
| 669 | . | ||
| 670 | |||
| 671 | MessageId=342 | ||
| 672 | Severity=Warning | ||
| 673 | SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE | ||
| 674 | Language=English | ||
| 675 | Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls! | ||
| 676 | . | ||
| 677 | |||
| 678 | MessageId=343 | ||
| 679 | Severity=Warning | ||
| 680 | SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE | ||
| 681 | Language=English | ||
| 682 | Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls! | ||
| 683 | . | ||
| 684 | |||
| 685 | MessageId=348 | ||
| 686 | Severity=Warning | ||
| 687 | SymbolicName=MSG_APPLY_RETRYING_PACKAGE | ||
| 688 | Language=English | ||
| 689 | Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying... | ||
| 690 | . | ||
| 691 | |||
| 692 | MessageId=349 | ||
| 693 | Severity=Warning | ||
| 694 | SymbolicName=MSG_APPLY_RETRYING_PAYLOAD | ||
| 695 | Language=English | ||
| 696 | Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying... | ||
| 697 | . | ||
| 698 | |||
| 699 | MessageId=350 | ||
| 700 | Severity=Warning | ||
| 701 | SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE | ||
| 702 | Language=English | ||
| 703 | Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... | ||
| 704 | . | ||
| 705 | |||
| 706 | MessageId=351 | ||
| 707 | Severity=Success | ||
| 708 | SymbolicName=MSG_UNCACHE_PACKAGE | ||
| 709 | Language=English | ||
| 710 | Removing cached package: %1!ls!, from path: %2!ls! | ||
| 711 | . | ||
| 712 | |||
| 713 | MessageId=352 | ||
| 714 | Severity=Success | ||
| 715 | SymbolicName=MSG_UNCACHE_BUNDLE | ||
| 716 | Language=English | ||
| 717 | Removing cached bundle: %1!ls!, from path: %2!ls! | ||
| 718 | . | ||
| 719 | |||
| 720 | MessageId=353 | ||
| 721 | Severity=Warning | ||
| 722 | SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE | ||
| 723 | Language=English | ||
| 724 | Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
| 725 | . | ||
| 726 | |||
| 727 | MessageId=354 | ||
| 728 | Severity=Warning | ||
| 729 | SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE | ||
| 730 | Language=English | ||
| 731 | Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
| 732 | . | ||
| 733 | |||
| 734 | MessageId=355 | ||
| 735 | Severity=Warning | ||
| 736 | SymbolicName=MSG_SOURCELIST_REGISTER | ||
| 737 | Language=English | ||
| 738 | Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... | ||
| 739 | . | ||
| 740 | |||
| 741 | MessageId=358 | ||
| 742 | Severity=Success | ||
| 743 | SymbolicName=MSG_PAUSE_AU_STARTING | ||
| 744 | Language=English | ||
| 745 | Pausing automatic updates. | ||
| 746 | . | ||
| 747 | |||
| 748 | MessageId=359 | ||
| 749 | Severity=Success | ||
| 750 | SymbolicName=MSG_PAUSE_AU_SUCCEEDED | ||
| 751 | Language=English | ||
| 752 | Paused automatic updates. | ||
| 753 | . | ||
| 754 | |||
| 755 | MessageId=360 | ||
| 756 | Severity=Success | ||
| 757 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING | ||
| 758 | Language=English | ||
| 759 | Creating a system restore point. | ||
| 760 | . | ||
| 761 | |||
| 762 | MessageId=361 | ||
| 763 | Severity=Success | ||
| 764 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED | ||
| 765 | Language=English | ||
| 766 | Created a system restore point. | ||
| 767 | . | ||
| 768 | |||
| 769 | MessageId=362 | ||
| 770 | Severity=Success | ||
| 771 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED | ||
| 772 | Language=English | ||
| 773 | System restore disabled, system restore point not created. | ||
| 774 | . | ||
| 775 | |||
| 776 | MessageId=363 | ||
| 777 | Severity=Warning | ||
| 778 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED | ||
| 779 | Language=English | ||
| 780 | Could not create system restore point, error: 0x%1!x!. Continuing... | ||
| 781 | . | ||
| 782 | |||
| 783 | MessageId=370 | ||
| 784 | Severity=Success | ||
| 785 | SymbolicName=MSG_SESSION_BEGIN | ||
| 786 | Language=English | ||
| 787 | Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! | ||
| 788 | . | ||
| 789 | |||
| 790 | MessageId=371 | ||
| 791 | Severity=Success | ||
| 792 | SymbolicName=MSG_SESSION_UPDATE | ||
| 793 | Language=English | ||
| 794 | Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! | ||
| 795 | . | ||
| 796 | |||
| 797 | MessageId=372 | ||
| 798 | Severity=Success | ||
| 799 | SymbolicName=MSG_SESSION_END | ||
| 800 | Language=English | ||
| 801 | Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! | ||
| 802 | . | ||
| 803 | |||
| 804 | MessageId=380 | ||
| 805 | Severity=Warning | ||
| 806 | SymbolicName=MSG_APPLY_SKIPPED | ||
| 807 | Language=English | ||
| 808 | Apply skipped, no planned actions | ||
| 809 | . | ||
| 810 | |||
| 811 | MessageId=381 | ||
| 812 | Severity=Warning | ||
| 813 | SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK | ||
| 814 | Language=English | ||
| 815 | Ignoring application request to cancel from %1!ls! during rollback. | ||
| 816 | . | ||
| 817 | |||
| 818 | MessageId=399 | ||
| 819 | Severity=Success | ||
| 820 | SymbolicName=MSG_APPLY_COMPLETE | ||
| 821 | Language=English | ||
| 822 | Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! | ||
| 823 | . | ||
| 824 | |||
| 825 | MessageId=400 | ||
| 826 | Severity=Success | ||
| 827 | SymbolicName=MSG_SYSTEM_SHUTDOWN | ||
| 828 | Language=English | ||
| 829 | Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! | ||
| 830 | . | ||
| 831 | |||
| 832 | MessageId=410 | ||
| 833 | Severity=Success | ||
| 834 | SymbolicName=MSG_VARIABLE_DUMP | ||
| 835 | Language=English | ||
| 836 | Variable: %1!ls! | ||
| 837 | . | ||
| 838 | |||
| 839 | MessageId=420 | ||
| 840 | Severity=Success | ||
| 841 | SymbolicName=MSG_RESUME_AU_STARTING | ||
| 842 | Language=English | ||
| 843 | Resuming automatic updates. | ||
| 844 | . | ||
| 845 | |||
| 846 | MessageId=421 | ||
| 847 | Severity=Success | ||
| 848 | SymbolicName=MSG_RESUME_AU_SUCCEEDED | ||
| 849 | Language=English | ||
| 850 | Resumed automatic updates. | ||
| 851 | . | ||
| 852 | |||
| 853 | MessageId=500 | ||
| 854 | Severity=Success | ||
| 855 | SymbolicName=MSG_QUIT | ||
| 856 | Language=English | ||
| 857 | Shutting down, exit code: 0x%1!x! | ||
| 858 | . | ||
| 859 | |||
| 860 | MessageId=501 | ||
| 861 | Severity=Warning | ||
| 862 | SymbolicName=MSG_STATE_NOT_SAVED | ||
| 863 | Language=English | ||
| 864 | The state file could not be saved, error: 0x%1!x!. Continuing... | ||
| 865 | . | ||
| 866 | |||
| 867 | MessageId=600 | ||
| 868 | Severity=Success | ||
| 869 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN | ||
| 870 | Language=English | ||
| 871 | LaunchApprovedExe begin, id: %1!ls! | ||
| 872 | . | ||
| 873 | |||
| 874 | MessageId=601 | ||
| 875 | Severity=Success | ||
| 876 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH | ||
| 877 | Language=English | ||
| 878 | Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! | ||
| 879 | . | ||
| 880 | |||
| 881 | MessageId=602 | ||
| 882 | Severity=Success | ||
| 883 | SymbolicName=MSG_LAUNCHING_APPROVED_EXE | ||
| 884 | Language=English | ||
| 885 | Launching approved exe, path: '%1!ls!', 'command: %2!ls!' | ||
| 886 | . | ||
| 887 | |||
| 888 | MessageId=699 | ||
| 889 | Severity=Success | ||
| 890 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE | ||
| 891 | Language=English | ||
| 892 | LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! | ||
| 893 | . | ||
| 894 | |||
| 895 | MessageId=700 | ||
| 896 | Severity=Success | ||
| 897 | SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED | ||
| 898 | Language=English | ||
| 899 | Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!. | ||
| 900 | . | ||
| 901 | |||
diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp new file mode 100644 index 00000000..71540d5d --- /dev/null +++ b/src/engine/exeengine.cpp | |||
| @@ -0,0 +1,820 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | static HRESULT HandleExitCode( | ||
| 9 | __in BURN_PACKAGE* pPackage, | ||
| 10 | __in DWORD dwExitCode, | ||
| 11 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 12 | ); | ||
| 13 | static HRESULT ParseCommandLineArgumentsFromXml( | ||
| 14 | __in IXMLDOMNode* pixnExePackage, | ||
| 15 | __in BURN_PACKAGE* pPackage | ||
| 16 | ); | ||
| 17 | static HRESULT ParseExitCodesFromXml( | ||
| 18 | __in IXMLDOMNode* pixnExePackage, | ||
| 19 | __in BURN_PACKAGE* pPackage | ||
| 20 | ); | ||
| 21 | |||
| 22 | |||
| 23 | // function definitions | ||
| 24 | |||
| 25 | extern "C" HRESULT ExeEngineParsePackageFromXml( | ||
| 26 | __in IXMLDOMNode* pixnExePackage, | ||
| 27 | __in BURN_PACKAGE* pPackage | ||
| 28 | ) | ||
| 29 | { | ||
| 30 | HRESULT hr = S_OK; | ||
| 31 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 32 | IXMLDOMNode* pixnNode = NULL; | ||
| 33 | LPWSTR scz = NULL; | ||
| 34 | |||
| 35 | // @DetectCondition | ||
| 36 | hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition); | ||
| 37 | ExitOnFailure(hr, "Failed to get @DetectCondition."); | ||
| 38 | |||
| 39 | // @InstallArguments | ||
| 40 | hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments); | ||
| 41 | ExitOnFailure(hr, "Failed to get @InstallArguments."); | ||
| 42 | |||
| 43 | // @UninstallArguments | ||
| 44 | hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments); | ||
| 45 | ExitOnFailure(hr, "Failed to get @UninstallArguments."); | ||
| 46 | |||
| 47 | // @RepairArguments | ||
| 48 | hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments); | ||
| 49 | ExitOnFailure(hr, "Failed to get @RepairArguments."); | ||
| 50 | |||
| 51 | // @Repairable | ||
| 52 | hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable); | ||
| 53 | if (E_NOTFOUND != hr) | ||
| 54 | { | ||
| 55 | ExitOnFailure(hr, "Failed to get @Repairable."); | ||
| 56 | } | ||
| 57 | |||
| 58 | // @Protocol | ||
| 59 | hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz); | ||
| 60 | if (SUCCEEDED(hr)) | ||
| 61 | { | ||
| 62 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1)) | ||
| 63 | { | ||
| 64 | pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN; | ||
| 65 | } | ||
| 66 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1)) | ||
| 67 | { | ||
| 68 | pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4; | ||
| 69 | } | ||
| 70 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1)) | ||
| 71 | { | ||
| 72 | pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE; | ||
| 73 | } | ||
| 74 | else | ||
| 75 | { | ||
| 76 | hr = E_UNEXPECTED; | ||
| 77 | ExitOnFailure(hr, "Invalid protocol type: %ls", scz); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | else if (E_NOTFOUND != hr) | ||
| 81 | { | ||
| 82 | ExitOnFailure(hr, "Failed to get @Protocol."); | ||
| 83 | } | ||
| 84 | |||
| 85 | hr = ParseExitCodesFromXml(pixnExePackage, pPackage); | ||
| 86 | ExitOnFailure(hr, "Failed to parse exit codes."); | ||
| 87 | |||
| 88 | hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage); | ||
| 89 | ExitOnFailure(hr, "Failed to parse command lines."); | ||
| 90 | |||
| 91 | LExit: | ||
| 92 | ReleaseObject(pixnNodes); | ||
| 93 | ReleaseObject(pixnNode); | ||
| 94 | ReleaseStr(scz); | ||
| 95 | |||
| 96 | return hr; | ||
| 97 | } | ||
| 98 | |||
| 99 | extern "C" void ExeEnginePackageUninitialize( | ||
| 100 | __in BURN_PACKAGE* pPackage | ||
| 101 | ) | ||
| 102 | { | ||
| 103 | ReleaseStr(pPackage->Exe.sczDetectCondition); | ||
| 104 | ReleaseStr(pPackage->Exe.sczInstallArguments); | ||
| 105 | ReleaseStr(pPackage->Exe.sczRepairArguments); | ||
| 106 | ReleaseStr(pPackage->Exe.sczUninstallArguments); | ||
| 107 | ReleaseStr(pPackage->Exe.sczIgnoreDependencies); | ||
| 108 | ReleaseStr(pPackage->Exe.sczAncestors); | ||
| 109 | //ReleaseStr(pPackage->Exe.sczProgressSwitch); | ||
| 110 | ReleaseMem(pPackage->Exe.rgExitCodes); | ||
| 111 | |||
| 112 | // free command-line arguments | ||
| 113 | if (pPackage->Exe.rgCommandLineArguments) | ||
| 114 | { | ||
| 115 | for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i) | ||
| 116 | { | ||
| 117 | BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; | ||
| 118 | ReleaseStr(pCommandLineArgument->sczInstallArgument); | ||
| 119 | ReleaseStr(pCommandLineArgument->sczUninstallArgument); | ||
| 120 | ReleaseStr(pCommandLineArgument->sczRepairArgument); | ||
| 121 | ReleaseStr(pCommandLineArgument->sczCondition); | ||
| 122 | } | ||
| 123 | MemFree(pPackage->Exe.rgCommandLineArguments); | ||
| 124 | } | ||
| 125 | |||
| 126 | // clear struct | ||
| 127 | memset(&pPackage->Exe, 0, sizeof(pPackage->Exe)); | ||
| 128 | } | ||
| 129 | |||
| 130 | extern "C" HRESULT ExeEngineDetectPackage( | ||
| 131 | __in BURN_PACKAGE* pPackage, | ||
| 132 | __in BURN_VARIABLES* pVariables | ||
| 133 | ) | ||
| 134 | { | ||
| 135 | HRESULT hr = S_OK; | ||
| 136 | BOOL fDetected = FALSE; | ||
| 137 | |||
| 138 | // evaluate detect condition | ||
| 139 | if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition) | ||
| 140 | { | ||
| 141 | hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected); | ||
| 142 | ExitOnFailure(hr, "Failed to evaluate executable package detect condition."); | ||
| 143 | } | ||
| 144 | |||
| 145 | // update detect state | ||
| 146 | pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 147 | |||
| 148 | LExit: | ||
| 149 | return hr; | ||
| 150 | } | ||
| 151 | |||
| 152 | // | ||
| 153 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
| 154 | // | ||
| 155 | extern "C" HRESULT ExeEnginePlanCalculatePackage( | ||
| 156 | __in BURN_PACKAGE* pPackage, | ||
| 157 | __out_opt BOOL* pfBARequestedCache | ||
| 158 | ) | ||
| 159 | { | ||
| 160 | HRESULT hr = S_OK; | ||
| 161 | //BOOL fCondition = FALSE; | ||
| 162 | //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; | ||
| 163 | BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 164 | BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 165 | BOOL fBARequestedCache = FALSE; | ||
| 166 | |||
| 167 | //// evaluate rollback install condition | ||
| 168 | //if (pPackage->sczRollbackInstallCondition) | ||
| 169 | //{ | ||
| 170 | // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition); | ||
| 171 | // ExitOnFailure(hr, "Failed to evaluate rollback install condition."); | ||
| 172 | |||
| 173 | // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 174 | //} | ||
| 175 | |||
| 176 | // execute action | ||
| 177 | switch (pPackage->currentState) | ||
| 178 | { | ||
| 179 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 180 | switch (pPackage->requested) | ||
| 181 | { | ||
| 182 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: | ||
| 183 | execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 184 | break; | ||
| 185 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 186 | execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 187 | break; | ||
| 188 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; | ||
| 189 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 190 | execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 191 | break; | ||
| 192 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: | ||
| 193 | execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; | ||
| 194 | break; | ||
| 195 | default: | ||
| 196 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | break; | ||
| 200 | |||
| 201 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 202 | switch (pPackage->requested) | ||
| 203 | { | ||
| 204 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 205 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 206 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 207 | break; | ||
| 208 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 209 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 210 | fBARequestedCache = TRUE; | ||
| 211 | break; | ||
| 212 | default: | ||
| 213 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | break; | ||
| 217 | |||
| 218 | default: | ||
| 219 | hr = E_INVALIDARG; | ||
| 220 | ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState); | ||
| 221 | } | ||
| 222 | |||
| 223 | // Calculate the rollback action if there is an execute action. | ||
| 224 | if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) | ||
| 225 | { | ||
| 226 | switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) | ||
| 227 | { | ||
| 228 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 229 | switch (pPackage->requested) | ||
| 230 | { | ||
| 231 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 232 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 233 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 234 | break; | ||
| 235 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
| 236 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 237 | rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 238 | break; | ||
| 239 | default: | ||
| 240 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 241 | break; | ||
| 242 | } | ||
| 243 | break; | ||
| 244 | |||
| 245 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 246 | switch (pPackage->requested) | ||
| 247 | { | ||
| 248 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 249 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 250 | rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 251 | break; | ||
| 252 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
| 253 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 254 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 255 | break; | ||
| 256 | default: | ||
| 257 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | break; | ||
| 261 | |||
| 262 | default: | ||
| 263 | hr = E_INVALIDARG; | ||
| 264 | ExitOnRootFailure(hr, "Invalid package expected state."); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | // return values | ||
| 269 | pPackage->execute = execute; | ||
| 270 | pPackage->rollback = rollback; | ||
| 271 | |||
| 272 | if (pfBARequestedCache) | ||
| 273 | { | ||
| 274 | *pfBARequestedCache = fBARequestedCache; | ||
| 275 | } | ||
| 276 | |||
| 277 | LExit: | ||
| 278 | return hr; | ||
| 279 | } | ||
| 280 | |||
| 281 | // | ||
| 282 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
| 283 | // | ||
| 284 | extern "C" HRESULT ExeEnginePlanAddPackage( | ||
| 285 | __in_opt DWORD *pdwInsertSequence, | ||
| 286 | __in BURN_PACKAGE* pPackage, | ||
| 287 | __in BURN_PLAN* pPlan, | ||
| 288 | __in BURN_LOGGING* pLog, | ||
| 289 | __in BURN_VARIABLES* pVariables, | ||
| 290 | __in_opt HANDLE hCacheEvent, | ||
| 291 | __in BOOL fPlanPackageCacheRollback | ||
| 292 | ) | ||
| 293 | { | ||
| 294 | HRESULT hr = S_OK; | ||
| 295 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 296 | |||
| 297 | // add wait for cache | ||
| 298 | if (hCacheEvent) | ||
| 299 | { | ||
| 300 | hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); | ||
| 301 | ExitOnFailure(hr, "Failed to plan package cache syncpoint"); | ||
| 302 | } | ||
| 303 | |||
| 304 | hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan); | ||
| 305 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
| 306 | |||
| 307 | // add execute action | ||
| 308 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) | ||
| 309 | { | ||
| 310 | if (NULL != pdwInsertSequence) | ||
| 311 | { | ||
| 312 | hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction); | ||
| 313 | ExitOnFailure(hr, "Failed to insert execute action."); | ||
| 314 | } | ||
| 315 | else | ||
| 316 | { | ||
| 317 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 318 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 319 | } | ||
| 320 | |||
| 321 | pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; | ||
| 322 | pAction->exePackage.pPackage = pPackage; | ||
| 323 | pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action); | ||
| 324 | pAction->exePackage.action = pPackage->execute; | ||
| 325 | |||
| 326 | if (pPackage->Exe.sczIgnoreDependencies) | ||
| 327 | { | ||
| 328 | hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); | ||
| 329 | ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); | ||
| 330 | } | ||
| 331 | |||
| 332 | if (pPackage->Exe.sczAncestors) | ||
| 333 | { | ||
| 334 | hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); | ||
| 335 | ExitOnFailure(hr, "Failed to allocate the list of ancestors."); | ||
| 336 | } | ||
| 337 | |||
| 338 | LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. | ||
| 339 | } | ||
| 340 | |||
| 341 | // add rollback action | ||
| 342 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) | ||
| 343 | { | ||
| 344 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 345 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 346 | |||
| 347 | pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; | ||
| 348 | pAction->exePackage.pPackage = pPackage; | ||
| 349 | pAction->exePackage.action = pPackage->rollback; | ||
| 350 | |||
| 351 | if (pPackage->Exe.sczIgnoreDependencies) | ||
| 352 | { | ||
| 353 | hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0); | ||
| 354 | ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore."); | ||
| 355 | } | ||
| 356 | |||
| 357 | if (pPackage->Exe.sczAncestors) | ||
| 358 | { | ||
| 359 | hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0); | ||
| 360 | ExitOnFailure(hr, "Failed to allocate the list of ancestors."); | ||
| 361 | } | ||
| 362 | |||
| 363 | LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors. | ||
| 364 | } | ||
| 365 | |||
| 366 | LExit: | ||
| 367 | return hr; | ||
| 368 | } | ||
| 369 | |||
| 370 | extern "C" HRESULT ExeEngineExecutePackage( | ||
| 371 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 372 | __in BURN_VARIABLES* pVariables, | ||
| 373 | __in BOOL fRollback, | ||
| 374 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 375 | __in LPVOID pvContext, | ||
| 376 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 377 | ) | ||
| 378 | { | ||
| 379 | HRESULT hr = S_OK; | ||
| 380 | WCHAR wzCurrentDirectory[MAX_PATH] = { }; | ||
| 381 | BOOL fChangedCurrentDirectory = FALSE; | ||
| 382 | int nResult = IDNOACTION; | ||
| 383 | LPCWSTR wzArguments = NULL; | ||
| 384 | LPWSTR sczArguments = NULL; | ||
| 385 | LPWSTR sczArgumentsFormatted = NULL; | ||
| 386 | LPWSTR sczArgumentsObfuscated = NULL; | ||
| 387 | LPWSTR sczCachedDirectory = NULL; | ||
| 388 | LPWSTR sczExecutablePath = NULL; | ||
| 389 | LPWSTR sczCommand = NULL; | ||
| 390 | LPWSTR sczCommandObfuscated = NULL; | ||
| 391 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; | ||
| 392 | STARTUPINFOW si = { }; | ||
| 393 | PROCESS_INFORMATION pi = { }; | ||
| 394 | DWORD dwExitCode = 0; | ||
| 395 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 396 | |||
| 397 | // get cached executable path | ||
| 398 | hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); | ||
| 399 | ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); | ||
| 400 | |||
| 401 | // Best effort to set the execute package cache folder and action variables. | ||
| 402 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); | ||
| 403 | VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE); | ||
| 404 | |||
| 405 | hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); | ||
| 406 | ExitOnFailure(hr, "Failed to build executable path."); | ||
| 407 | |||
| 408 | // pick arguments | ||
| 409 | switch (pExecuteAction->exePackage.action) | ||
| 410 | { | ||
| 411 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
| 412 | wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; | ||
| 413 | break; | ||
| 414 | |||
| 415 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 416 | wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; | ||
| 417 | break; | ||
| 418 | |||
| 419 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | ||
| 420 | wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; | ||
| 421 | break; | ||
| 422 | |||
| 423 | default: | ||
| 424 | hr = E_INVALIDARG; | ||
| 425 | ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); | ||
| 426 | } | ||
| 427 | |||
| 428 | // now add optional arguments | ||
| 429 | hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0); | ||
| 430 | ExitOnFailure(hr, "Failed to copy package arguments."); | ||
| 431 | |||
| 432 | for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i) | ||
| 433 | { | ||
| 434 | BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i]; | ||
| 435 | BOOL fCondition = FALSE; | ||
| 436 | |||
| 437 | hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition); | ||
| 438 | ExitOnFailure(hr, "Failed to evaluate executable package command-line condition."); | ||
| 439 | |||
| 440 | if (fCondition) | ||
| 441 | { | ||
| 442 | hr = StrAllocConcat(&sczArguments, L" ", 0); | ||
| 443 | ExitOnFailure(hr, "Failed to separate command-line arguments."); | ||
| 444 | |||
| 445 | switch (pExecuteAction->exePackage.action) | ||
| 446 | { | ||
| 447 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
| 448 | hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0); | ||
| 449 | ExitOnFailure(hr, "Failed to get command-line argument for install."); | ||
| 450 | break; | ||
| 451 | |||
| 452 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 453 | hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0); | ||
| 454 | ExitOnFailure(hr, "Failed to get command-line argument for uninstall."); | ||
| 455 | break; | ||
| 456 | |||
| 457 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | ||
| 458 | hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0); | ||
| 459 | ExitOnFailure(hr, "Failed to get command-line argument for repair."); | ||
| 460 | break; | ||
| 461 | |||
| 462 | default: | ||
| 463 | hr = E_INVALIDARG; | ||
| 464 | ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action); | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | // build command | ||
| 470 | if (0 < lstrlenW(sczArguments)) | ||
| 471 | { | ||
| 472 | hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL); | ||
| 473 | ExitOnFailure(hr, "Failed to format argument string."); | ||
| 474 | |||
| 475 | hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); | ||
| 476 | ExitOnFailure(hr, "Failed to create executable command."); | ||
| 477 | |||
| 478 | hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL); | ||
| 479 | ExitOnFailure(hr, "Failed to format obfuscated argument string."); | ||
| 480 | |||
| 481 | hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); | ||
| 482 | } | ||
| 483 | else | ||
| 484 | { | ||
| 485 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); | ||
| 486 | ExitOnFailure(hr, "Failed to create executable command."); | ||
| 487 | |||
| 488 | hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); | ||
| 489 | } | ||
| 490 | ExitOnFailure(hr, "Failed to create obfuscated executable command."); | ||
| 491 | |||
| 492 | if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors) | ||
| 493 | { | ||
| 494 | // Add the list of dependencies to ignore, if any, to the burn command line. | ||
| 495 | if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) | ||
| 496 | { | ||
| 497 | hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); | ||
| 498 | ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); | ||
| 499 | |||
| 500 | hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); | ||
| 501 | ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); | ||
| 502 | } | ||
| 503 | |||
| 504 | // Add the list of ancestors, if any, to the burn command line. | ||
| 505 | if (pExecuteAction->exePackage.sczAncestors) | ||
| 506 | { | ||
| 507 | hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); | ||
| 508 | ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); | ||
| 509 | |||
| 510 | hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); | ||
| 511 | ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) | ||
| 516 | { | ||
| 517 | hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); | ||
| 518 | ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); | ||
| 519 | } | ||
| 520 | |||
| 521 | // Log before we add the secret pipe name and client token for embedded processes. | ||
| 522 | LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); | ||
| 523 | |||
| 524 | if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) | ||
| 525 | { | ||
| 526 | hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); | ||
| 527 | ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); | ||
| 528 | } | ||
| 529 | else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) | ||
| 530 | { | ||
| 531 | hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); | ||
| 532 | ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); | ||
| 533 | } | ||
| 534 | else // create and wait for the executable process while sending fake progress to allow cancel. | ||
| 535 | { | ||
| 536 | // Make the cache location of the executable the current directory to help those executables | ||
| 537 | // that expect stuff to be relative to them. | ||
| 538 | if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) | ||
| 539 | { | ||
| 540 | fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); | ||
| 541 | } | ||
| 542 | |||
| 543 | si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? | ||
| 544 | if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) | ||
| 545 | { | ||
| 546 | ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); | ||
| 547 | } | ||
| 548 | |||
| 549 | if (pExecuteAction->exePackage.fFireAndForget) | ||
| 550 | { | ||
| 551 | ::WaitForInputIdle(pi.hProcess, 5000); | ||
| 552 | ExitFunction(); | ||
| 553 | } | ||
| 554 | |||
| 555 | do | ||
| 556 | { | ||
| 557 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 558 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 559 | message.progress.dwPercentage = 50; | ||
| 560 | nResult = pfnGenericMessageHandler(&message, pvContext); | ||
| 561 | hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
| 562 | ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); | ||
| 563 | |||
| 564 | hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); | ||
| 565 | if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) | ||
| 566 | { | ||
| 567 | ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); | ||
| 568 | } | ||
| 569 | } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); | ||
| 570 | } | ||
| 571 | |||
| 572 | hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); | ||
| 573 | ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode); | ||
| 574 | |||
| 575 | LExit: | ||
| 576 | if (fChangedCurrentDirectory) | ||
| 577 | { | ||
| 578 | ::SetCurrentDirectoryW(wzCurrentDirectory); | ||
| 579 | } | ||
| 580 | |||
| 581 | StrSecureZeroFreeString(sczArguments); | ||
| 582 | StrSecureZeroFreeString(sczArgumentsFormatted); | ||
| 583 | ReleaseStr(sczArgumentsObfuscated); | ||
| 584 | ReleaseStr(sczCachedDirectory); | ||
| 585 | ReleaseStr(sczExecutablePath); | ||
| 586 | StrSecureZeroFreeString(sczCommand); | ||
| 587 | ReleaseStr(sczCommandObfuscated); | ||
| 588 | |||
| 589 | ReleaseHandle(pi.hThread); | ||
| 590 | ReleaseHandle(pi.hProcess); | ||
| 591 | ReleaseFileHandle(hExecutableFile); | ||
| 592 | |||
| 593 | // Best effort to clear the execute package cache folder and action variables. | ||
| 594 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); | ||
| 595 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); | ||
| 596 | |||
| 597 | return hr; | ||
| 598 | } | ||
| 599 | |||
| 600 | |||
| 601 | // internal helper functions | ||
| 602 | |||
| 603 | static HRESULT ParseExitCodesFromXml( | ||
| 604 | __in IXMLDOMNode* pixnExePackage, | ||
| 605 | __in BURN_PACKAGE* pPackage | ||
| 606 | ) | ||
| 607 | { | ||
| 608 | HRESULT hr = S_OK; | ||
| 609 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 610 | IXMLDOMNode* pixnNode = NULL; | ||
| 611 | DWORD cNodes = 0; | ||
| 612 | LPWSTR scz = NULL; | ||
| 613 | |||
| 614 | // select exit code nodes | ||
| 615 | hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes); | ||
| 616 | ExitOnFailure(hr, "Failed to select exit code nodes."); | ||
| 617 | |||
| 618 | // get exit code node count | ||
| 619 | hr = pixnNodes->get_length((long*) &cNodes); | ||
| 620 | ExitOnFailure(hr, "Failed to get exit code node count."); | ||
| 621 | |||
| 622 | if (cNodes) | ||
| 623 | { | ||
| 624 | // allocate memory for exit codes | ||
| 625 | pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE); | ||
| 626 | ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs."); | ||
| 627 | |||
| 628 | pPackage->Exe.cExitCodes = cNodes; | ||
| 629 | |||
| 630 | // parse package elements | ||
| 631 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 632 | { | ||
| 633 | BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; | ||
| 634 | |||
| 635 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 636 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 637 | |||
| 638 | // @Type | ||
| 639 | hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type); | ||
| 640 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 641 | |||
| 642 | // @Code | ||
| 643 | hr = XmlGetAttributeEx(pixnNode, L"Code", &scz); | ||
| 644 | ExitOnFailure(hr, "Failed to get @Code."); | ||
| 645 | |||
| 646 | if (L'*' == scz[0]) | ||
| 647 | { | ||
| 648 | pExitCode->fWildcard = TRUE; | ||
| 649 | } | ||
| 650 | else | ||
| 651 | { | ||
| 652 | hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode); | ||
| 653 | ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz); | ||
| 654 | } | ||
| 655 | |||
| 656 | // prepare next iteration | ||
| 657 | ReleaseNullObject(pixnNode); | ||
| 658 | } | ||
| 659 | } | ||
| 660 | |||
| 661 | hr = S_OK; | ||
| 662 | |||
| 663 | LExit: | ||
| 664 | ReleaseObject(pixnNodes); | ||
| 665 | ReleaseObject(pixnNode); | ||
| 666 | ReleaseStr(scz); | ||
| 667 | |||
| 668 | return hr; | ||
| 669 | } | ||
| 670 | |||
| 671 | static HRESULT ParseCommandLineArgumentsFromXml( | ||
| 672 | __in IXMLDOMNode* pixnExePackage, | ||
| 673 | __in BURN_PACKAGE* pPackage | ||
| 674 | ) | ||
| 675 | { | ||
| 676 | HRESULT hr = S_OK; | ||
| 677 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 678 | IXMLDOMNode* pixnNode = NULL; | ||
| 679 | DWORD cNodes = 0; | ||
| 680 | LPWSTR scz = NULL; | ||
| 681 | |||
| 682 | // Select command-line argument nodes. | ||
| 683 | hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes); | ||
| 684 | ExitOnFailure(hr, "Failed to select command-line argument nodes."); | ||
| 685 | |||
| 686 | // Get command-line argument node count. | ||
| 687 | hr = pixnNodes->get_length((long*) &cNodes); | ||
| 688 | ExitOnFailure(hr, "Failed to get command-line argument count."); | ||
| 689 | |||
| 690 | if (cNodes) | ||
| 691 | { | ||
| 692 | pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE); | ||
| 693 | ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs."); | ||
| 694 | |||
| 695 | pPackage->Exe.cCommandLineArguments = cNodes; | ||
| 696 | |||
| 697 | // Parse command-line argument elements. | ||
| 698 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 699 | { | ||
| 700 | BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i]; | ||
| 701 | |||
| 702 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 703 | ExitOnFailure(hr, "Failed to get next command-line argument node."); | ||
| 704 | |||
| 705 | // @InstallArgument | ||
| 706 | hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument); | ||
| 707 | ExitOnFailure(hr, "Failed to get @InstallArgument."); | ||
| 708 | |||
| 709 | // @UninstallArgument | ||
| 710 | hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument); | ||
| 711 | ExitOnFailure(hr, "Failed to get @UninstallArgument."); | ||
| 712 | |||
| 713 | // @RepairArgument | ||
| 714 | hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument); | ||
| 715 | ExitOnFailure(hr, "Failed to get @RepairArgument."); | ||
| 716 | |||
| 717 | // @Condition | ||
| 718 | hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition); | ||
| 719 | ExitOnFailure(hr, "Failed to get @Condition."); | ||
| 720 | |||
| 721 | // Prepare next iteration. | ||
| 722 | ReleaseNullObject(pixnNode); | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | hr = S_OK; | ||
| 727 | |||
| 728 | LExit: | ||
| 729 | ReleaseObject(pixnNodes); | ||
| 730 | ReleaseObject(pixnNode); | ||
| 731 | ReleaseStr(scz); | ||
| 732 | |||
| 733 | return hr; | ||
| 734 | } | ||
| 735 | |||
| 736 | static HRESULT HandleExitCode( | ||
| 737 | __in BURN_PACKAGE* pPackage, | ||
| 738 | __in DWORD dwExitCode, | ||
| 739 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 740 | ) | ||
| 741 | { | ||
| 742 | HRESULT hr = S_OK; | ||
| 743 | BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE; | ||
| 744 | |||
| 745 | for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i) | ||
| 746 | { | ||
| 747 | BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i]; | ||
| 748 | |||
| 749 | // If this is a wildcard, use the last one we come across. | ||
| 750 | if (pExitCode->fWildcard) | ||
| 751 | { | ||
| 752 | typeCode = pExitCode->type; | ||
| 753 | } | ||
| 754 | else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking. | ||
| 755 | { | ||
| 756 | typeCode = pExitCode->type; | ||
| 757 | break; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts | ||
| 762 | // and everything else as an error. | ||
| 763 | if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode) | ||
| 764 | { | ||
| 765 | if (0 == dwExitCode) | ||
| 766 | { | ||
| 767 | typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS; | ||
| 768 | } | ||
| 769 | else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode || | ||
| 770 | HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) || | ||
| 771 | ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode || | ||
| 772 | HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode)) | ||
| 773 | { | ||
| 774 | typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT; | ||
| 775 | } | ||
| 776 | else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode || | ||
| 777 | HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode)) | ||
| 778 | { | ||
| 779 | typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT; | ||
| 780 | } | ||
| 781 | else | ||
| 782 | { | ||
| 783 | typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR; | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | switch (typeCode) | ||
| 788 | { | ||
| 789 | case BURN_EXE_EXIT_CODE_TYPE_SUCCESS: | ||
| 790 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 791 | hr = S_OK; | ||
| 792 | break; | ||
| 793 | |||
| 794 | case BURN_EXE_EXIT_CODE_TYPE_ERROR: | ||
| 795 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 796 | hr = HRESULT_FROM_WIN32(dwExitCode); | ||
| 797 | if (SUCCEEDED(hr)) | ||
| 798 | { | ||
| 799 | hr = E_FAIL; | ||
| 800 | } | ||
| 801 | break; | ||
| 802 | |||
| 803 | case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT: | ||
| 804 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 805 | hr = S_OK; | ||
| 806 | break; | ||
| 807 | |||
| 808 | case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT: | ||
| 809 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
| 810 | hr = S_OK; | ||
| 811 | break; | ||
| 812 | |||
| 813 | default: | ||
| 814 | hr = E_UNEXPECTED; | ||
| 815 | break; | ||
| 816 | } | ||
| 817 | |||
| 818 | //LExit: | ||
| 819 | return hr; | ||
| 820 | } | ||
diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h new file mode 100644 index 00000000..402e51ed --- /dev/null +++ b/src/engine/exeengine.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // function declarations | ||
| 11 | |||
| 12 | HRESULT ExeEngineParsePackageFromXml( | ||
| 13 | __in IXMLDOMNode* pixnExePackage, | ||
| 14 | __in BURN_PACKAGE* pPackage | ||
| 15 | ); | ||
| 16 | void ExeEnginePackageUninitialize( | ||
| 17 | __in BURN_PACKAGE* pPackage | ||
| 18 | ); | ||
| 19 | HRESULT ExeEngineDetectPackage( | ||
| 20 | __in BURN_PACKAGE* pPackage, | ||
| 21 | __in BURN_VARIABLES* pVariables | ||
| 22 | ); | ||
| 23 | HRESULT ExeEnginePlanCalculatePackage( | ||
| 24 | __in BURN_PACKAGE* pPackage, | ||
| 25 | __out_opt BOOL* pfBARequestedCache | ||
| 26 | ); | ||
| 27 | HRESULT ExeEnginePlanAddPackage( | ||
| 28 | __in_opt DWORD *pdwInsertSequence, | ||
| 29 | __in BURN_PACKAGE* pPackage, | ||
| 30 | __in BURN_PLAN* pPlan, | ||
| 31 | __in BURN_LOGGING* pLog, | ||
| 32 | __in BURN_VARIABLES* pVariables, | ||
| 33 | __in_opt HANDLE hCacheEvent, | ||
| 34 | __in BOOL fPlanPackageCacheRollback | ||
| 35 | ); | ||
| 36 | HRESULT ExeEngineExecutePackage( | ||
| 37 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 38 | __in BURN_VARIABLES* pVariables, | ||
| 39 | __in BOOL fRollback, | ||
| 40 | __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress, | ||
| 41 | __in LPVOID pvContext, | ||
| 42 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 43 | ); | ||
| 44 | |||
| 45 | |||
| 46 | #if defined(__cplusplus) | ||
| 47 | } | ||
| 48 | #endif | ||
diff --git a/src/engine/inc/engine.h b/src/engine/inc/engine.h new file mode 100644 index 00000000..808bb91a --- /dev/null +++ b/src/engine/inc/engine.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // function declarations | ||
| 11 | |||
| 12 | BOOL EngineInCleanRoom( | ||
| 13 | __in_z_opt LPCWSTR wzCommandLine | ||
| 14 | ); | ||
| 15 | |||
| 16 | HRESULT EngineRun( | ||
| 17 | __in HINSTANCE hInstance, | ||
| 18 | __in HANDLE hEngineFile, | ||
| 19 | __in_z_opt LPCWSTR wzCommandLine, | ||
| 20 | __in int nCmdShow, | ||
| 21 | __out DWORD* pdwExitCode | ||
| 22 | ); | ||
| 23 | |||
| 24 | |||
| 25 | #if defined(__cplusplus) | ||
| 26 | } | ||
| 27 | #endif | ||
diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp new file mode 100644 index 00000000..818532ad --- /dev/null +++ b/src/engine/logging.cpp | |||
| @@ -0,0 +1,683 @@ | |||
| 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 | |||
| 5 | |||
| 6 | static DWORD vdwPackageSequence = 0; | ||
| 7 | static const DWORD LOG_OPEN_RETRY_COUNT = 3; | ||
| 8 | static const DWORD LOG_OPEN_RETRY_WAIT = 2000; | ||
| 9 | static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file."; | ||
| 10 | |||
| 11 | // structs | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | // internal function declarations | ||
| 16 | |||
| 17 | static void CheckLoggingPolicy( | ||
| 18 | __out DWORD *pdwAttributes | ||
| 19 | ); | ||
| 20 | static HRESULT GetNonSessionSpecificTempFolder( | ||
| 21 | __deref_out_z LPWSTR* psczNonSessionTempFolder | ||
| 22 | ); | ||
| 23 | |||
| 24 | |||
| 25 | // function definitions | ||
| 26 | |||
| 27 | extern "C" HRESULT LoggingOpen( | ||
| 28 | __in BURN_LOGGING* pLog, | ||
| 29 | __in BURN_VARIABLES* pVariables, | ||
| 30 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 31 | __in_z LPCWSTR wzBundleName | ||
| 32 | ) | ||
| 33 | { | ||
| 34 | HRESULT hr = S_OK; | ||
| 35 | LPWSTR sczLoggingBaseFolder = NULL; | ||
| 36 | |||
| 37 | // Check if the logging policy is set and configure the logging appropriately. | ||
| 38 | CheckLoggingPolicy(&pLog->dwAttributes); | ||
| 39 | |||
| 40 | if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) | ||
| 41 | { | ||
| 42 | if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) | ||
| 43 | { | ||
| 44 | LogSetLevel(REPORT_DEBUG, FALSE); | ||
| 45 | } | ||
| 46 | else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE) | ||
| 47 | { | ||
| 48 | LogSetLevel(REPORT_VERBOSE, FALSE); | ||
| 49 | } | ||
| 50 | |||
| 51 | if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix)) | ||
| 52 | { | ||
| 53 | PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | // Open the log approriately. | ||
| 58 | if (pLog->sczPath && *pLog->sczPath) | ||
| 59 | { | ||
| 60 | DWORD cRetry = 0; | ||
| 61 | |||
| 62 | hr = DirGetCurrent(&sczLoggingBaseFolder); | ||
| 63 | ExitOnFailure(hr, "Failed to get current directory."); | ||
| 64 | |||
| 65 | // Try pretty hard to open the log file when appending. | ||
| 66 | do | ||
| 67 | { | ||
| 68 | if (0 < cRetry) | ||
| 69 | { | ||
| 70 | ::Sleep(LOG_OPEN_RETRY_WAIT); | ||
| 71 | } | ||
| 72 | |||
| 73 | hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath); | ||
| 74 | if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr) | ||
| 75 | { | ||
| 76 | ++cRetry; | ||
| 77 | } | ||
| 78 | } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT); | ||
| 79 | |||
| 80 | if (FAILED(hr)) | ||
| 81 | { | ||
| 82 | // Log is not open, so note that. | ||
| 83 | LogDisable(); | ||
| 84 | pLog->state = BURN_LOGGING_STATE_DISABLED; | ||
| 85 | |||
| 86 | if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND) | ||
| 87 | { | ||
| 88 | // If appending, ignore the failure and continue. | ||
| 89 | hr = S_OK; | ||
| 90 | } | ||
| 91 | else // specifically tried to create a log file so show an error if appropriate and bail. | ||
| 92 | { | ||
| 93 | HRESULT hrOriginal = hr; | ||
| 94 | |||
| 95 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE); | ||
| 96 | SplashScreenDisplayError(display, wzBundleName, hr); | ||
| 97 | |||
| 98 | ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | else | ||
| 102 | { | ||
| 103 | pLog->state = BURN_LOGGING_STATE_OPEN; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | else if (pLog->sczPrefix && *pLog->sczPrefix) | ||
| 107 | { | ||
| 108 | hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder); | ||
| 109 | ExitOnFailure(hr, "Failed to get non-session specific TEMP folder."); | ||
| 110 | |||
| 111 | // Best effort to open default logging. | ||
| 112 | hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); | ||
| 113 | if (FAILED(hr)) | ||
| 114 | { | ||
| 115 | LogDisable(); | ||
| 116 | pLog->state = BURN_LOGGING_STATE_DISABLED; | ||
| 117 | |||
| 118 | hr = S_OK; | ||
| 119 | } | ||
| 120 | else | ||
| 121 | { | ||
| 122 | pLog->state = BURN_LOGGING_STATE_OPEN; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | else // no logging enabled. | ||
| 126 | { | ||
| 127 | LogDisable(); | ||
| 128 | pLog->state = BURN_LOGGING_STATE_DISABLED; | ||
| 129 | } | ||
| 130 | |||
| 131 | // If the log was opened, write the header info and update the prefix and extension to match | ||
| 132 | // the log name so future logs are opened with the same pattern. | ||
| 133 | if (BURN_LOGGING_STATE_OPEN == pLog->state) | ||
| 134 | { | ||
| 135 | LPCWSTR wzExtension = PathExtension(pLog->sczPath); | ||
| 136 | if (wzExtension && *wzExtension) | ||
| 137 | { | ||
| 138 | hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath); | ||
| 139 | ExitOnFailure(hr, "Failed to copy log path to prefix."); | ||
| 140 | |||
| 141 | hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0); | ||
| 142 | ExitOnFailure(hr, "Failed to copy log extension to extension."); | ||
| 143 | } | ||
| 144 | else | ||
| 145 | { | ||
| 146 | hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0); | ||
| 147 | ExitOnFailure(hr, "Failed to copy full log path to prefix."); | ||
| 148 | } | ||
| 149 | |||
| 150 | if (pLog->sczPathVariable && *pLog->sczPathVariable) | ||
| 151 | { | ||
| 152 | VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure. | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | LExit: | ||
| 157 | ReleaseStr(sczLoggingBaseFolder); | ||
| 158 | |||
| 159 | return hr; | ||
| 160 | } | ||
| 161 | |||
| 162 | extern "C" void LoggingOpenFailed() | ||
| 163 | { | ||
| 164 | HRESULT hr = S_OK; | ||
| 165 | HANDLE hEventLog = NULL; | ||
| 166 | LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE); | ||
| 167 | WORD wNumStrings = 1; | ||
| 168 | |||
| 169 | hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); | ||
| 170 | if (SUCCEEDED(hr)) | ||
| 171 | { | ||
| 172 | ExitFunction(); | ||
| 173 | } | ||
| 174 | |||
| 175 | // If opening the "failure" log failed, then attempt to record that in the Application event log. | ||
| 176 | hEventLog = ::OpenEventLogW(NULL, L"Application"); | ||
| 177 | ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log"); | ||
| 178 | |||
| 179 | hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL); | ||
| 180 | ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry"); | ||
| 181 | |||
| 182 | LExit: | ||
| 183 | if (hEventLog) | ||
| 184 | { | ||
| 185 | ::CloseEventLog(hEventLog); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | extern "C" void LoggingIncrementPackageSequence() | ||
| 190 | { | ||
| 191 | ++vdwPackageSequence; | ||
| 192 | } | ||
| 193 | |||
| 194 | extern "C" HRESULT LoggingSetPackageVariable( | ||
| 195 | __in BURN_PACKAGE* pPackage, | ||
| 196 | __in_z_opt LPCWSTR wzSuffix, | ||
| 197 | __in BOOL fRollback, | ||
| 198 | __in BURN_LOGGING* pLog, | ||
| 199 | __in BURN_VARIABLES* pVariables, | ||
| 200 | __out_opt LPWSTR* psczLogPath | ||
| 201 | ) | ||
| 202 | { | ||
| 203 | HRESULT hr = S_OK; | ||
| 204 | LPWSTR sczLogPath = NULL; | ||
| 205 | |||
| 206 | // Make sure that no package log files are created when logging has been disabled via Log element. | ||
| 207 | if (BURN_LOGGING_STATE_DISABLED == pLog->state) | ||
| 208 | { | ||
| 209 | if (psczLogPath) | ||
| 210 | { | ||
| 211 | *psczLogPath = NULL; | ||
| 212 | } | ||
| 213 | |||
| 214 | ExitFunction(); | ||
| 215 | } | ||
| 216 | |||
| 217 | if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) || | ||
| 218 | (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable)) | ||
| 219 | { | ||
| 220 | hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension); | ||
| 221 | ExitOnFailure(hr, "Failed to allocate path for package log."); | ||
| 222 | |||
| 223 | hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE); | ||
| 224 | ExitOnFailure(hr, "Failed to set log path into variable."); | ||
| 225 | |||
| 226 | if (psczLogPath) | ||
| 227 | { | ||
| 228 | hr = StrAllocString(psczLogPath, sczLogPath, 0); | ||
| 229 | ExitOnFailure(hr, "Failed to copy package log path."); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | LExit: | ||
| 234 | ReleaseStr(sczLogPath); | ||
| 235 | |||
| 236 | return hr; | ||
| 237 | } | ||
| 238 | |||
| 239 | extern "C" LPCSTR LoggingBurnActionToString( | ||
| 240 | __in BOOTSTRAPPER_ACTION action | ||
| 241 | ) | ||
| 242 | { | ||
| 243 | switch (action) | ||
| 244 | { | ||
| 245 | case BOOTSTRAPPER_ACTION_UNKNOWN: | ||
| 246 | return "Unknown"; | ||
| 247 | case BOOTSTRAPPER_ACTION_HELP: | ||
| 248 | return "Help"; | ||
| 249 | case BOOTSTRAPPER_ACTION_LAYOUT: | ||
| 250 | return "Layout"; | ||
| 251 | case BOOTSTRAPPER_ACTION_CACHE: | ||
| 252 | return "Cache"; | ||
| 253 | case BOOTSTRAPPER_ACTION_UNINSTALL: | ||
| 254 | return "Uninstall"; | ||
| 255 | case BOOTSTRAPPER_ACTION_INSTALL: | ||
| 256 | return "Install"; | ||
| 257 | case BOOTSTRAPPER_ACTION_MODIFY: | ||
| 258 | return "Modify"; | ||
| 259 | case BOOTSTRAPPER_ACTION_REPAIR: | ||
| 260 | return "Repair"; | ||
| 261 | case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: | ||
| 262 | return "UpdateReplace"; | ||
| 263 | case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: | ||
| 264 | return "UpdateReplaceEmbedded"; | ||
| 265 | default: | ||
| 266 | return "Invalid"; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | extern "C" LPCSTR LoggingActionStateToString( | ||
| 271 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
| 272 | ) | ||
| 273 | { | ||
| 274 | switch (actionState) | ||
| 275 | { | ||
| 276 | case BOOTSTRAPPER_ACTION_STATE_NONE: | ||
| 277 | return "None"; | ||
| 278 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 279 | return "Uninstall"; | ||
| 280 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
| 281 | return "Install"; | ||
| 282 | case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: | ||
| 283 | return "AdminInstall"; | ||
| 284 | case BOOTSTRAPPER_ACTION_STATE_MODIFY: | ||
| 285 | return "Modify"; | ||
| 286 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | ||
| 287 | return "Repair"; | ||
| 288 | case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: | ||
| 289 | return "MinorUpgrade"; | ||
| 290 | case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: | ||
| 291 | return "MajorUpgrade"; | ||
| 292 | case BOOTSTRAPPER_ACTION_STATE_PATCH: | ||
| 293 | return "Patch"; | ||
| 294 | default: | ||
| 295 | return "Invalid"; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | extern "C" LPCSTR LoggingDependencyActionToString( | ||
| 300 | BURN_DEPENDENCY_ACTION action | ||
| 301 | ) | ||
| 302 | { | ||
| 303 | switch (action) | ||
| 304 | { | ||
| 305 | case BURN_DEPENDENCY_ACTION_NONE: | ||
| 306 | return "None"; | ||
| 307 | case BURN_DEPENDENCY_ACTION_REGISTER: | ||
| 308 | return "Register"; | ||
| 309 | case BURN_DEPENDENCY_ACTION_UNREGISTER: | ||
| 310 | return "Unregister"; | ||
| 311 | default: | ||
| 312 | return "Invalid"; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | extern "C" LPCSTR LoggingBoolToString( | ||
| 317 | __in BOOL f | ||
| 318 | ) | ||
| 319 | { | ||
| 320 | if (f) | ||
| 321 | { | ||
| 322 | return "Yes"; | ||
| 323 | } | ||
| 324 | |||
| 325 | return "No"; | ||
| 326 | } | ||
| 327 | |||
| 328 | extern "C" LPCSTR LoggingTrueFalseToString( | ||
| 329 | __in BOOL f | ||
| 330 | ) | ||
| 331 | { | ||
| 332 | if (f) | ||
| 333 | { | ||
| 334 | return "true"; | ||
| 335 | } | ||
| 336 | |||
| 337 | return "false"; | ||
| 338 | } | ||
| 339 | |||
| 340 | extern "C" LPCSTR LoggingPackageStateToString( | ||
| 341 | __in BOOTSTRAPPER_PACKAGE_STATE packageState | ||
| 342 | ) | ||
| 343 | { | ||
| 344 | switch (packageState) | ||
| 345 | { | ||
| 346 | case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN: | ||
| 347 | return "Unknown"; | ||
| 348 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: | ||
| 349 | return "Obsolete"; | ||
| 350 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 351 | return "Absent"; | ||
| 352 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 353 | return "Cached"; | ||
| 354 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 355 | return "Present"; | ||
| 356 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 357 | return "Superseded"; | ||
| 358 | default: | ||
| 359 | return "Invalid"; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | extern "C" LPCSTR LoggingCacheStateToString( | ||
| 364 | __in BURN_CACHE_STATE cacheState | ||
| 365 | ) | ||
| 366 | { | ||
| 367 | switch (cacheState) | ||
| 368 | { | ||
| 369 | case BURN_CACHE_STATE_NONE: | ||
| 370 | return "None"; | ||
| 371 | case BURN_CACHE_STATE_PARTIAL: | ||
| 372 | return "Partial"; | ||
| 373 | case BURN_CACHE_STATE_COMPLETE: | ||
| 374 | return "Complete"; | ||
| 375 | default: | ||
| 376 | return "Invalid"; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | extern "C" LPCSTR LoggingMsiFeatureStateToString( | ||
| 381 | __in BOOTSTRAPPER_FEATURE_STATE featureState | ||
| 382 | ) | ||
| 383 | { | ||
| 384 | switch (featureState) | ||
| 385 | { | ||
| 386 | case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: | ||
| 387 | return "Unknown"; | ||
| 388 | case BOOTSTRAPPER_FEATURE_STATE_ABSENT: | ||
| 389 | return "Absent"; | ||
| 390 | case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: | ||
| 391 | return "Advertised"; | ||
| 392 | case BOOTSTRAPPER_FEATURE_STATE_LOCAL: | ||
| 393 | return "Local"; | ||
| 394 | case BOOTSTRAPPER_FEATURE_STATE_SOURCE: | ||
| 395 | return "Source"; | ||
| 396 | default: | ||
| 397 | return "Invalid"; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | extern "C" LPCSTR LoggingMsiFeatureActionToString( | ||
| 402 | __in BOOTSTRAPPER_FEATURE_ACTION featureAction | ||
| 403 | ) | ||
| 404 | { | ||
| 405 | switch (featureAction) | ||
| 406 | { | ||
| 407 | case BOOTSTRAPPER_FEATURE_ACTION_NONE: | ||
| 408 | return "None"; | ||
| 409 | case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: | ||
| 410 | return "AddLocal"; | ||
| 411 | case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: | ||
| 412 | return "AddSource"; | ||
| 413 | case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: | ||
| 414 | return "AddDefault"; | ||
| 415 | case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: | ||
| 416 | return "Reinstall"; | ||
| 417 | case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: | ||
| 418 | return "Advertise"; | ||
| 419 | case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: | ||
| 420 | return "Remove"; | ||
| 421 | default: | ||
| 422 | return "Invalid"; | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | extern "C" LPCSTR LoggingMsiInstallContext( | ||
| 427 | __in MSIINSTALLCONTEXT context | ||
| 428 | ) | ||
| 429 | { | ||
| 430 | switch (context) | ||
| 431 | { | ||
| 432 | case MSIINSTALLCONTEXT_ALL: | ||
| 433 | return "All"; | ||
| 434 | case MSIINSTALLCONTEXT_ALLUSERMANAGED: | ||
| 435 | return "AllUserManaged"; | ||
| 436 | case MSIINSTALLCONTEXT_MACHINE: | ||
| 437 | return "Machine"; | ||
| 438 | case MSIINSTALLCONTEXT_NONE: | ||
| 439 | return "None"; | ||
| 440 | case MSIINSTALLCONTEXT_USERMANAGED: | ||
| 441 | return "UserManaged"; | ||
| 442 | case MSIINSTALLCONTEXT_USERUNMANAGED: | ||
| 443 | return "UserUnmanaged"; | ||
| 444 | default: | ||
| 445 | return "Invalid"; | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | extern "C" LPCSTR LoggingPerMachineToString( | ||
| 450 | __in BOOL fPerMachine | ||
| 451 | ) | ||
| 452 | { | ||
| 453 | if (fPerMachine) | ||
| 454 | { | ||
| 455 | return "PerMachine"; | ||
| 456 | } | ||
| 457 | |||
| 458 | return "PerUser"; | ||
| 459 | } | ||
| 460 | |||
| 461 | extern "C" LPCSTR LoggingRestartToString( | ||
| 462 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
| 463 | ) | ||
| 464 | { | ||
| 465 | switch (restart) | ||
| 466 | { | ||
| 467 | case BOOTSTRAPPER_APPLY_RESTART_NONE: | ||
| 468 | return "None"; | ||
| 469 | case BOOTSTRAPPER_APPLY_RESTART_REQUIRED: | ||
| 470 | return "Required"; | ||
| 471 | case BOOTSTRAPPER_APPLY_RESTART_INITIATED: | ||
| 472 | return "Initiated"; | ||
| 473 | default: | ||
| 474 | return "Invalid"; | ||
| 475 | } | ||
| 476 | } | ||
| 477 | |||
| 478 | extern "C" LPCSTR LoggingResumeModeToString( | ||
| 479 | __in BURN_RESUME_MODE resumeMode | ||
| 480 | ) | ||
| 481 | { | ||
| 482 | switch (resumeMode) | ||
| 483 | { | ||
| 484 | case BURN_RESUME_MODE_NONE: | ||
| 485 | return "None"; | ||
| 486 | case BURN_RESUME_MODE_ACTIVE: | ||
| 487 | return "Active"; | ||
| 488 | case BURN_RESUME_MODE_SUSPEND: | ||
| 489 | return "Suspend"; | ||
| 490 | case BURN_RESUME_MODE_ARP: | ||
| 491 | return "ARP"; | ||
| 492 | case BURN_RESUME_MODE_REBOOT_PENDING: | ||
| 493 | return "Reboot Pending"; | ||
| 494 | default: | ||
| 495 | return "Invalid"; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | extern "C" LPCSTR LoggingRelationTypeToString( | ||
| 500 | __in BOOTSTRAPPER_RELATION_TYPE type | ||
| 501 | ) | ||
| 502 | { | ||
| 503 | switch (type) | ||
| 504 | { | ||
| 505 | case BOOTSTRAPPER_RELATION_NONE: | ||
| 506 | return "None"; | ||
| 507 | case BOOTSTRAPPER_RELATION_DETECT: | ||
| 508 | return "Detect"; | ||
| 509 | case BOOTSTRAPPER_RELATION_UPGRADE: | ||
| 510 | return "Upgrade"; | ||
| 511 | case BOOTSTRAPPER_RELATION_ADDON: | ||
| 512 | return "Addon"; | ||
| 513 | case BOOTSTRAPPER_RELATION_PATCH: | ||
| 514 | return "Patch"; | ||
| 515 | case BOOTSTRAPPER_RELATION_DEPENDENT: | ||
| 516 | return "Dependent"; | ||
| 517 | case BOOTSTRAPPER_RELATION_UPDATE: | ||
| 518 | return "Update"; | ||
| 519 | default: | ||
| 520 | return "Invalid"; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | extern "C" LPCSTR LoggingRelatedOperationToString( | ||
| 525 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 526 | ) | ||
| 527 | { | ||
| 528 | switch (operation) | ||
| 529 | { | ||
| 530 | case BOOTSTRAPPER_RELATED_OPERATION_NONE: | ||
| 531 | return "None"; | ||
| 532 | case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE: | ||
| 533 | return "Downgrade"; | ||
| 534 | case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE: | ||
| 535 | return "MinorUpdate"; | ||
| 536 | case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE: | ||
| 537 | return "MajorUpgrade"; | ||
| 538 | case BOOTSTRAPPER_RELATED_OPERATION_REMOVE: | ||
| 539 | return "Remove"; | ||
| 540 | case BOOTSTRAPPER_RELATED_OPERATION_INSTALL: | ||
| 541 | return "Install"; | ||
| 542 | case BOOTSTRAPPER_RELATED_OPERATION_REPAIR: | ||
| 543 | return "Repair"; | ||
| 544 | default: | ||
| 545 | return "Invalid"; | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | extern "C" LPCSTR LoggingRequestStateToString( | ||
| 550 | __in BOOTSTRAPPER_REQUEST_STATE requestState | ||
| 551 | ) | ||
| 552 | { | ||
| 553 | switch (requestState) | ||
| 554 | { | ||
| 555 | case BOOTSTRAPPER_REQUEST_STATE_NONE: | ||
| 556 | return "None"; | ||
| 557 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: | ||
| 558 | return "ForceAbsent"; | ||
| 559 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 560 | return "Absent"; | ||
| 561 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 562 | return "Cache"; | ||
| 563 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: | ||
| 564 | return "Present"; | ||
| 565 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 566 | return "Repair"; | ||
| 567 | default: | ||
| 568 | return "Invalid"; | ||
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | extern "C" LPCSTR LoggingRollbackOrExecute( | ||
| 573 | __in BOOL fRollback | ||
| 574 | ) | ||
| 575 | { | ||
| 576 | return fRollback ? "rollback" : "execute"; | ||
| 577 | } | ||
| 578 | |||
| 579 | extern "C" LPWSTR LoggingStringOrUnknownIfNull( | ||
| 580 | __in LPCWSTR wz | ||
| 581 | ) | ||
| 582 | { | ||
| 583 | return wz ? wz : L"Unknown"; | ||
| 584 | } | ||
| 585 | |||
| 586 | // Note: this function is not thread safe. | ||
| 587 | extern "C" LPCSTR LoggingVersionToString( | ||
| 588 | __in DWORD64 dw64Version | ||
| 589 | ) | ||
| 590 | { | ||
| 591 | static CHAR szVersion[40] = { 0 }; | ||
| 592 | HRESULT hr = S_OK; | ||
| 593 | |||
| 594 | hr = ::StringCchPrintfA(szVersion, countof(szVersion), "%I64u.%I64u.%I64u.%I64u", dw64Version >> 48 & 0xFFFF, dw64Version >> 32 & 0xFFFF, dw64Version >> 16 & 0xFFFF, dw64Version & 0xFFFF); | ||
| 595 | if (FAILED(hr)) | ||
| 596 | { | ||
| 597 | memset(szVersion, 0, sizeof(szVersion)); | ||
| 598 | } | ||
| 599 | |||
| 600 | return szVersion; | ||
| 601 | } | ||
| 602 | |||
| 603 | |||
| 604 | // internal function declarations | ||
| 605 | |||
| 606 | static void CheckLoggingPolicy( | ||
| 607 | __out DWORD *pdwAttributes | ||
| 608 | ) | ||
| 609 | { | ||
| 610 | HRESULT hr = S_OK; | ||
| 611 | HKEY hk = NULL; | ||
| 612 | LPWSTR sczLoggingPolicy = NULL; | ||
| 613 | |||
| 614 | hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk); | ||
| 615 | if (SUCCEEDED(hr)) | ||
| 616 | { | ||
| 617 | hr = RegReadString(hk, L"Logging", &sczLoggingPolicy); | ||
| 618 | if (SUCCEEDED(hr)) | ||
| 619 | { | ||
| 620 | LPCWSTR wz = sczLoggingPolicy; | ||
| 621 | while (*wz) | ||
| 622 | { | ||
| 623 | if (L'v' == *wz || L'V' == *wz) | ||
| 624 | { | ||
| 625 | *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE; | ||
| 626 | } | ||
| 627 | else if (L'x' == *wz || L'X' == *wz) | ||
| 628 | { | ||
| 629 | *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG; | ||
| 630 | } | ||
| 631 | |||
| 632 | ++wz; | ||
| 633 | } | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | ReleaseStr(sczLoggingPolicy); | ||
| 638 | ReleaseRegKey(hk); | ||
| 639 | } | ||
| 640 | |||
| 641 | static HRESULT GetNonSessionSpecificTempFolder( | ||
| 642 | __deref_out_z LPWSTR* psczNonSessionTempFolder | ||
| 643 | ) | ||
| 644 | { | ||
| 645 | HRESULT hr = S_OK; | ||
| 646 | WCHAR wzTempFolder[MAX_PATH] = { }; | ||
| 647 | DWORD cchTempFolder = 0; | ||
| 648 | DWORD dwSessionId = 0; | ||
| 649 | LPWSTR sczSessionId = 0; | ||
| 650 | DWORD cchSessionId = 0; | ||
| 651 | |||
| 652 | if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) | ||
| 653 | { | ||
| 654 | ExitWithLastError(hr, "Failed to get temp folder."); | ||
| 655 | } | ||
| 656 | |||
| 657 | hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast<size_t*>(&cchTempFolder)); | ||
| 658 | ExitOnFailure(hr, "Failed to get length of temp folder."); | ||
| 659 | |||
| 660 | // If our session id is in the TEMP path then remove that part so we get the non-session | ||
| 661 | // specific temporary folder. | ||
| 662 | if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId)) | ||
| 663 | { | ||
| 664 | hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId); | ||
| 665 | ExitOnFailure(hr, "Failed to format session id as a string."); | ||
| 666 | |||
| 667 | hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId)); | ||
| 668 | ExitOnFailure(hr, "Failed to get length of session id string."); | ||
| 669 | |||
| 670 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, cchSessionId, sczSessionId, cchSessionId)) | ||
| 671 | { | ||
| 672 | cchTempFolder -= cchSessionId; | ||
| 673 | } | ||
| 674 | } | ||
| 675 | |||
| 676 | hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder); | ||
| 677 | ExitOnFailure(hr, "Failed to copy temp folder."); | ||
| 678 | |||
| 679 | LExit: | ||
| 680 | ReleaseStr(sczSessionId); | ||
| 681 | |||
| 682 | return hr; | ||
| 683 | } | ||
diff --git a/src/engine/logging.h b/src/engine/logging.h new file mode 100644 index 00000000..cea4d31d --- /dev/null +++ b/src/engine/logging.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | enum BURN_LOGGING_STATE | ||
| 13 | { | ||
| 14 | BURN_LOGGING_STATE_CLOSED, | ||
| 15 | BURN_LOGGING_STATE_OPEN, | ||
| 16 | BURN_LOGGING_STATE_DISABLED, | ||
| 17 | }; | ||
| 18 | |||
| 19 | enum BURN_LOGGING_ATTRIBUTE | ||
| 20 | { | ||
| 21 | BURN_LOGGING_ATTRIBUTE_APPEND = 0x1, | ||
| 22 | BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2, | ||
| 23 | BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4, | ||
| 24 | }; | ||
| 25 | |||
| 26 | |||
| 27 | // structs | ||
| 28 | |||
| 29 | typedef struct _BURN_LOGGING | ||
| 30 | { | ||
| 31 | BURN_LOGGING_STATE state; | ||
| 32 | LPWSTR sczPathVariable; | ||
| 33 | |||
| 34 | DWORD dwAttributes; | ||
| 35 | LPWSTR sczPath; | ||
| 36 | LPWSTR sczPrefix; | ||
| 37 | LPWSTR sczExtension; | ||
| 38 | } BURN_LOGGING; | ||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | // function declarations | ||
| 43 | |||
| 44 | HRESULT LoggingOpen( | ||
| 45 | __in BURN_LOGGING* pLog, | ||
| 46 | __in BURN_VARIABLES* pVariables, | ||
| 47 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 48 | __in_z LPCWSTR wzBundleName | ||
| 49 | ); | ||
| 50 | |||
| 51 | void LoggingOpenFailed(); | ||
| 52 | |||
| 53 | void LoggingIncrementPackageSequence(); | ||
| 54 | |||
| 55 | HRESULT LoggingSetPackageVariable( | ||
| 56 | __in BURN_PACKAGE* pPackage, | ||
| 57 | __in_z_opt LPCWSTR wzSuffix, | ||
| 58 | __in BOOL fRollback, | ||
| 59 | __in BURN_LOGGING* pLog, | ||
| 60 | __in BURN_VARIABLES* pVariables, | ||
| 61 | __out_opt LPWSTR* psczLogPath | ||
| 62 | ); | ||
| 63 | |||
| 64 | LPCSTR LoggingBurnActionToString( | ||
| 65 | __in BOOTSTRAPPER_ACTION action | ||
| 66 | ); | ||
| 67 | |||
| 68 | LPCSTR LoggingActionStateToString( | ||
| 69 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
| 70 | ); | ||
| 71 | |||
| 72 | LPCSTR LoggingDependencyActionToString( | ||
| 73 | BURN_DEPENDENCY_ACTION action | ||
| 74 | ); | ||
| 75 | |||
| 76 | LPCSTR LoggingBoolToString( | ||
| 77 | __in BOOL f | ||
| 78 | ); | ||
| 79 | |||
| 80 | LPCSTR LoggingTrueFalseToString( | ||
| 81 | __in BOOL f | ||
| 82 | ); | ||
| 83 | |||
| 84 | LPCSTR LoggingPackageStateToString( | ||
| 85 | __in BOOTSTRAPPER_PACKAGE_STATE packageState | ||
| 86 | ); | ||
| 87 | |||
| 88 | LPCSTR LoggingCacheStateToString( | ||
| 89 | __in BURN_CACHE_STATE cacheState | ||
| 90 | ); | ||
| 91 | |||
| 92 | LPCSTR LoggingMsiFeatureStateToString( | ||
| 93 | __in BOOTSTRAPPER_FEATURE_STATE featureState | ||
| 94 | ); | ||
| 95 | |||
| 96 | LPCSTR LoggingMsiFeatureActionToString( | ||
| 97 | __in BOOTSTRAPPER_FEATURE_ACTION featureAction | ||
| 98 | ); | ||
| 99 | |||
| 100 | LPCSTR LoggingMsiInstallContext( | ||
| 101 | __in MSIINSTALLCONTEXT context | ||
| 102 | ); | ||
| 103 | |||
| 104 | LPCSTR LoggingPerMachineToString( | ||
| 105 | __in BOOL fPerMachine | ||
| 106 | ); | ||
| 107 | |||
| 108 | LPCSTR LoggingRestartToString( | ||
| 109 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
| 110 | ); | ||
| 111 | |||
| 112 | LPCSTR LoggingResumeModeToString( | ||
| 113 | __in BURN_RESUME_MODE resumeMode | ||
| 114 | ); | ||
| 115 | |||
| 116 | LPCSTR LoggingRelationTypeToString( | ||
| 117 | __in BOOTSTRAPPER_RELATION_TYPE type | ||
| 118 | ); | ||
| 119 | |||
| 120 | LPCSTR LoggingRelatedOperationToString( | ||
| 121 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 122 | ); | ||
| 123 | |||
| 124 | LPCSTR LoggingRequestStateToString( | ||
| 125 | __in BOOTSTRAPPER_REQUEST_STATE requestState | ||
| 126 | ); | ||
| 127 | |||
| 128 | LPCSTR LoggingRollbackOrExecute( | ||
| 129 | __in BOOL fRollback | ||
| 130 | ); | ||
| 131 | |||
| 132 | LPWSTR LoggingStringOrUnknownIfNull( | ||
| 133 | __in LPCWSTR wz | ||
| 134 | ); | ||
| 135 | |||
| 136 | // Note: this function is not thread safe. | ||
| 137 | LPCSTR LoggingVersionToString( | ||
| 138 | __in DWORD64 dw64Version | ||
| 139 | ); | ||
| 140 | |||
| 141 | |||
| 142 | #if defined(__cplusplus) | ||
| 143 | } | ||
| 144 | #endif | ||
diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp new file mode 100644 index 00000000..c2214a89 --- /dev/null +++ b/src/engine/manifest.cpp | |||
| @@ -0,0 +1,125 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // function definitions | ||
| 7 | |||
| 8 | extern "C" HRESULT ManifestLoadXmlFromBuffer( | ||
| 9 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 10 | __in SIZE_T cbBuffer, | ||
| 11 | __in BURN_ENGINE_STATE* pEngineState | ||
| 12 | ) | ||
| 13 | { | ||
| 14 | HRESULT hr = S_OK; | ||
| 15 | IXMLDOMDocument* pixdDocument = NULL; | ||
| 16 | IXMLDOMElement* pixeBundle = NULL; | ||
| 17 | IXMLDOMNode* pixnLog = NULL; | ||
| 18 | IXMLDOMNode* pixnChain = NULL; | ||
| 19 | |||
| 20 | // load xml document | ||
| 21 | hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument); | ||
| 22 | ExitOnFailure(hr, "Failed to load manifest as XML document."); | ||
| 23 | |||
| 24 | // get bundle element | ||
| 25 | hr = pixdDocument->get_documentElement(&pixeBundle); | ||
| 26 | ExitOnFailure(hr, "Failed to get bundle element."); | ||
| 27 | |||
| 28 | // parse the log element, if present. | ||
| 29 | hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog); | ||
| 30 | ExitOnFailure(hr, "Failed to get Log element."); | ||
| 31 | |||
| 32 | if (S_OK == hr) | ||
| 33 | { | ||
| 34 | hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable); | ||
| 35 | if (E_NOTFOUND != hr) | ||
| 36 | { | ||
| 37 | ExitOnFailure(hr, "Failed to get Log/@PathVariable."); | ||
| 38 | } | ||
| 39 | |||
| 40 | hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix); | ||
| 41 | ExitOnFailure(hr, "Failed to get Log/@Prefix attribute."); | ||
| 42 | |||
| 43 | hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension); | ||
| 44 | ExitOnFailure(hr, "Failed to get Log/@Extension attribute."); | ||
| 45 | } | ||
| 46 | |||
| 47 | // get the chain element | ||
| 48 | hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain); | ||
| 49 | ExitOnFailure(hr, "Failed to get chain element."); | ||
| 50 | |||
| 51 | if (S_OK == hr) | ||
| 52 | { | ||
| 53 | // parse disable rollback | ||
| 54 | hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback); | ||
| 55 | if (E_NOTFOUND != hr) | ||
| 56 | { | ||
| 57 | ExitOnFailure(hr, "Failed to get Chain/@DisableRollback"); | ||
| 58 | } | ||
| 59 | |||
| 60 | // parse disable system restore | ||
| 61 | hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore); | ||
| 62 | if (E_NOTFOUND != hr) | ||
| 63 | { | ||
| 64 | ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore"); | ||
| 65 | } | ||
| 66 | |||
| 67 | // parse parallel cache | ||
| 68 | hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute); | ||
| 69 | if (E_NOTFOUND != hr) | ||
| 70 | { | ||
| 71 | ExitOnFailure(hr, "Failed to get Chain/@ParallelCache"); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | // parse built-in condition | ||
| 76 | hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle); | ||
| 77 | ExitOnFailure(hr, "Failed to parse global condition."); | ||
| 78 | |||
| 79 | // parse variables | ||
| 80 | hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle); | ||
| 81 | ExitOnFailure(hr, "Failed to parse variables."); | ||
| 82 | |||
| 83 | // parse searches | ||
| 84 | hr = SearchesParseFromXml(&pEngineState->searches, pixeBundle); // TODO: Modularization | ||
| 85 | ExitOnFailure(hr, "Failed to parse searches."); | ||
| 86 | |||
| 87 | // parse user experience | ||
| 88 | hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle); | ||
| 89 | ExitOnFailure(hr, "Failed to parse user experience."); | ||
| 90 | |||
| 91 | // parse catalog files | ||
| 92 | hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle); | ||
| 93 | ExitOnFailure(hr, "Failed to parse catalog files."); | ||
| 94 | |||
| 95 | // parse registration | ||
| 96 | hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle); | ||
| 97 | ExitOnFailure(hr, "Failed to parse registration."); | ||
| 98 | |||
| 99 | // parse update | ||
| 100 | hr = UpdateParseFromXml(&pEngineState->update, pixeBundle); | ||
| 101 | ExitOnFailure(hr, "Failed to parse update."); | ||
| 102 | |||
| 103 | // parse containers | ||
| 104 | hr = ContainersParseFromXml(&pEngineState->section, &pEngineState->containers, pixeBundle); | ||
| 105 | ExitOnFailure(hr, "Failed to parse containers."); | ||
| 106 | |||
| 107 | // parse payloads | ||
| 108 | hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->catalogs, pixeBundle); | ||
| 109 | ExitOnFailure(hr, "Failed to parse payloads."); | ||
| 110 | |||
| 111 | // parse packages | ||
| 112 | hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle); | ||
| 113 | ExitOnFailure(hr, "Failed to parse packages."); | ||
| 114 | |||
| 115 | // parse approved exes for elevation | ||
| 116 | hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle); | ||
| 117 | ExitOnFailure(hr, "Failed to parse approved exes."); | ||
| 118 | |||
| 119 | LExit: | ||
| 120 | ReleaseObject(pixnChain); | ||
| 121 | ReleaseObject(pixnLog); | ||
| 122 | ReleaseObject(pixeBundle); | ||
| 123 | ReleaseObject(pixdDocument); | ||
| 124 | return hr; | ||
| 125 | } | ||
diff --git a/src/engine/manifest.h b/src/engine/manifest.h new file mode 100644 index 00000000..6e535d60 --- /dev/null +++ b/src/engine/manifest.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | interface IBurnPayload; // forward declare. | ||
| 6 | |||
| 7 | #if defined(__cplusplus) | ||
| 8 | extern "C" { | ||
| 9 | #endif | ||
| 10 | |||
| 11 | |||
| 12 | // function declarations | ||
| 13 | |||
| 14 | HRESULT ManifestLoadXmlFromBuffer( | ||
| 15 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 16 | __in SIZE_T cbBuffer, | ||
| 17 | __in BURN_ENGINE_STATE* pEngineState | ||
| 18 | ); | ||
| 19 | |||
| 20 | |||
| 21 | #if defined(__cplusplus) | ||
| 22 | } | ||
| 23 | #endif | ||
diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp new file mode 100644 index 00000000..17571ac5 --- /dev/null +++ b/src/engine/msiengine.cpp | |||
| @@ -0,0 +1,1910 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | |||
| 9 | // structs | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | // internal function declarations | ||
| 14 | |||
| 15 | static HRESULT ParseRelatedMsiFromXml( | ||
| 16 | __in IXMLDOMNode* pixnRelatedMsi, | ||
| 17 | __in BURN_RELATED_MSI* pRelatedMsi | ||
| 18 | ); | ||
| 19 | static HRESULT EvaluateActionStateConditions( | ||
| 20 | __in BURN_VARIABLES* pVariables, | ||
| 21 | __in_z_opt LPCWSTR sczAddLocalCondition, | ||
| 22 | __in_z_opt LPCWSTR sczAddSourceCondition, | ||
| 23 | __in_z_opt LPCWSTR sczAdvertiseCondition, | ||
| 24 | __out BOOTSTRAPPER_FEATURE_STATE* pState | ||
| 25 | ); | ||
| 26 | static HRESULT CalculateFeatureAction( | ||
| 27 | __in BOOTSTRAPPER_FEATURE_STATE currentState, | ||
| 28 | __in BOOTSTRAPPER_FEATURE_STATE requestedState, | ||
| 29 | __in BOOL fRepair, | ||
| 30 | __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, | ||
| 31 | __inout BOOL* pfDelta | ||
| 32 | ); | ||
| 33 | static HRESULT EscapePropertyArgumentString( | ||
| 34 | __in LPCWSTR wzProperty, | ||
| 35 | __inout_z LPWSTR* psczEscapedValue, | ||
| 36 | __in BOOL fZeroOnRealloc | ||
| 37 | ); | ||
| 38 | static HRESULT ConcatFeatureActionProperties( | ||
| 39 | __in BURN_PACKAGE* pPackage, | ||
| 40 | __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, | ||
| 41 | __inout_z LPWSTR* psczArguments | ||
| 42 | ); | ||
| 43 | static HRESULT ConcatPatchProperty( | ||
| 44 | __in BURN_PACKAGE* pPackage, | ||
| 45 | __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, | ||
| 46 | __inout_z LPWSTR* psczArguments | ||
| 47 | ); | ||
| 48 | static void RegisterSourceDirectory( | ||
| 49 | __in BURN_PACKAGE* pPackage, | ||
| 50 | __in_z LPCWSTR wzCacheDirectory | ||
| 51 | ); | ||
| 52 | |||
| 53 | |||
| 54 | // function definitions | ||
| 55 | |||
| 56 | extern "C" HRESULT MsiEngineParsePackageFromXml( | ||
| 57 | __in IXMLDOMNode* pixnMsiPackage, | ||
| 58 | __in BURN_PACKAGE* pPackage | ||
| 59 | ) | ||
| 60 | { | ||
| 61 | HRESULT hr = S_OK; | ||
| 62 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 63 | IXMLDOMNode* pixnNode = NULL; | ||
| 64 | DWORD cNodes = 0; | ||
| 65 | LPWSTR scz = NULL; | ||
| 66 | |||
| 67 | // @ProductCode | ||
| 68 | hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode); | ||
| 69 | ExitOnFailure(hr, "Failed to get @ProductCode."); | ||
| 70 | |||
| 71 | // @Language | ||
| 72 | hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage); | ||
| 73 | ExitOnFailure(hr, "Failed to get @Language."); | ||
| 74 | |||
| 75 | // @Version | ||
| 76 | hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); | ||
| 77 | ExitOnFailure(hr, "Failed to get @Version."); | ||
| 78 | |||
| 79 | hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion); | ||
| 80 | ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); | ||
| 81 | |||
| 82 | // @DisplayInternalUI | ||
| 83 | hr = XmlGetYesNoAttribute(pixnMsiPackage, L"DisplayInternalUI", &pPackage->Msi.fDisplayInternalUI); | ||
| 84 | ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); | ||
| 85 | |||
| 86 | // @UpgradeCode | ||
| 87 | hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); | ||
| 88 | if (E_NOTFOUND != hr) | ||
| 89 | { | ||
| 90 | ExitOnFailure(hr, "Failed to get @UpgradeCode."); | ||
| 91 | } | ||
| 92 | |||
| 93 | // select feature nodes | ||
| 94 | hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes); | ||
| 95 | ExitOnFailure(hr, "Failed to select feature nodes."); | ||
| 96 | |||
| 97 | // get feature node count | ||
| 98 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 99 | ExitOnFailure(hr, "Failed to get feature node count."); | ||
| 100 | |||
| 101 | if (cNodes) | ||
| 102 | { | ||
| 103 | // allocate memory for features | ||
| 104 | pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE); | ||
| 105 | ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs."); | ||
| 106 | |||
| 107 | pPackage->Msi.cFeatures = cNodes; | ||
| 108 | |||
| 109 | // parse feature elements | ||
| 110 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 111 | { | ||
| 112 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 113 | |||
| 114 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 115 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 116 | |||
| 117 | // @Id | ||
| 118 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId); | ||
| 119 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 120 | |||
| 121 | // @AddLocalCondition | ||
| 122 | hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition); | ||
| 123 | if (E_NOTFOUND != hr) | ||
| 124 | { | ||
| 125 | ExitOnFailure(hr, "Failed to get @AddLocalCondition."); | ||
| 126 | } | ||
| 127 | |||
| 128 | // @AddSourceCondition | ||
| 129 | hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition); | ||
| 130 | if (E_NOTFOUND != hr) | ||
| 131 | { | ||
| 132 | ExitOnFailure(hr, "Failed to get @AddSourceCondition."); | ||
| 133 | } | ||
| 134 | |||
| 135 | // @AdvertiseCondition | ||
| 136 | hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition); | ||
| 137 | if (E_NOTFOUND != hr) | ||
| 138 | { | ||
| 139 | ExitOnFailure(hr, "Failed to get @AdvertiseCondition."); | ||
| 140 | } | ||
| 141 | |||
| 142 | // @RollbackAddLocalCondition | ||
| 143 | hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition); | ||
| 144 | if (E_NOTFOUND != hr) | ||
| 145 | { | ||
| 146 | ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition."); | ||
| 147 | } | ||
| 148 | |||
| 149 | // @RollbackAddSourceCondition | ||
| 150 | hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition); | ||
| 151 | if (E_NOTFOUND != hr) | ||
| 152 | { | ||
| 153 | ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition."); | ||
| 154 | } | ||
| 155 | |||
| 156 | // @RollbackAdvertiseCondition | ||
| 157 | hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition); | ||
| 158 | if (E_NOTFOUND != hr) | ||
| 159 | { | ||
| 160 | ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition."); | ||
| 161 | } | ||
| 162 | |||
| 163 | // prepare next iteration | ||
| 164 | ReleaseNullObject(pixnNode); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | ReleaseNullObject(pixnNodes); // done with the MsiFeature elements. | ||
| 169 | |||
| 170 | hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties); | ||
| 171 | ExitOnFailure(hr, "Failed to parse properties from XML."); | ||
| 172 | |||
| 173 | // select related MSI nodes | ||
| 174 | hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes); | ||
| 175 | ExitOnFailure(hr, "Failed to select related MSI nodes."); | ||
| 176 | |||
| 177 | // get related MSI node count | ||
| 178 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 179 | ExitOnFailure(hr, "Failed to get related MSI node count."); | ||
| 180 | |||
| 181 | if (cNodes) | ||
| 182 | { | ||
| 183 | // allocate memory for related MSIs | ||
| 184 | pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE); | ||
| 185 | ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs."); | ||
| 186 | |||
| 187 | pPackage->Msi.cRelatedMsis = cNodes; | ||
| 188 | |||
| 189 | // parse related MSI elements | ||
| 190 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 191 | { | ||
| 192 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 193 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 194 | |||
| 195 | // parse related MSI element | ||
| 196 | hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]); | ||
| 197 | ExitOnFailure(hr, "Failed to parse related MSI element."); | ||
| 198 | |||
| 199 | // prepare next iteration | ||
| 200 | ReleaseNullObject(pixnNode); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements. | ||
| 205 | |||
| 206 | // Select slipstream MSP nodes. | ||
| 207 | hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes); | ||
| 208 | ExitOnFailure(hr, "Failed to select related MSI nodes."); | ||
| 209 | |||
| 210 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 211 | ExitOnFailure(hr, "Failed to get related MSI node count."); | ||
| 212 | |||
| 213 | if (cNodes) | ||
| 214 | { | ||
| 215 | pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE)); | ||
| 216 | ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); | ||
| 217 | |||
| 218 | pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); | ||
| 219 | ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); | ||
| 220 | |||
| 221 | pPackage->Msi.cSlipstreamMspPackages = cNodes; | ||
| 222 | |||
| 223 | // Parse slipstream MSP Ids. | ||
| 224 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 225 | { | ||
| 226 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 227 | ExitOnFailure(hr, "Failed to get next slipstream MSP node."); | ||
| 228 | |||
| 229 | hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i); | ||
| 230 | ExitOnFailure(hr, "Failed to parse slipstream MSP ids."); | ||
| 231 | |||
| 232 | ReleaseNullObject(pixnNode); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | hr = S_OK; | ||
| 237 | |||
| 238 | LExit: | ||
| 239 | ReleaseObject(pixnNodes); | ||
| 240 | ReleaseObject(pixnNode); | ||
| 241 | ReleaseStr(scz); | ||
| 242 | |||
| 243 | return hr; | ||
| 244 | } | ||
| 245 | |||
| 246 | extern "C" HRESULT MsiEngineParsePropertiesFromXml( | ||
| 247 | __in IXMLDOMNode* pixnPackage, | ||
| 248 | __out BURN_MSIPROPERTY** prgProperties, | ||
| 249 | __out DWORD* pcProperties | ||
| 250 | ) | ||
| 251 | { | ||
| 252 | HRESULT hr = S_OK; | ||
| 253 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 254 | IXMLDOMNode* pixnNode = NULL; | ||
| 255 | DWORD cNodes = 0; | ||
| 256 | |||
| 257 | BURN_MSIPROPERTY* pProperties = NULL; | ||
| 258 | |||
| 259 | // select property nodes | ||
| 260 | hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes); | ||
| 261 | ExitOnFailure(hr, "Failed to select property nodes."); | ||
| 262 | |||
| 263 | // get property node count | ||
| 264 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 265 | ExitOnFailure(hr, "Failed to get property node count."); | ||
| 266 | |||
| 267 | if (cNodes) | ||
| 268 | { | ||
| 269 | // allocate memory for properties | ||
| 270 | pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE); | ||
| 271 | ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs."); | ||
| 272 | |||
| 273 | // parse property elements | ||
| 274 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 275 | { | ||
| 276 | BURN_MSIPROPERTY* pProperty = &pProperties[i]; | ||
| 277 | |||
| 278 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 279 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 280 | |||
| 281 | // @Id | ||
| 282 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId); | ||
| 283 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 284 | |||
| 285 | // @Value | ||
| 286 | hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue); | ||
| 287 | ExitOnFailure(hr, "Failed to get @Value."); | ||
| 288 | |||
| 289 | // @RollbackValue | ||
| 290 | hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue); | ||
| 291 | if (E_NOTFOUND != hr) | ||
| 292 | { | ||
| 293 | ExitOnFailure(hr, "Failed to get @RollbackValue."); | ||
| 294 | } | ||
| 295 | |||
| 296 | // @Condition | ||
| 297 | hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition); | ||
| 298 | if (E_NOTFOUND != hr) | ||
| 299 | { | ||
| 300 | ExitOnFailure(hr, "Failed to get @Condition."); | ||
| 301 | } | ||
| 302 | |||
| 303 | // prepare next iteration | ||
| 304 | ReleaseNullObject(pixnNode); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | *pcProperties = cNodes; | ||
| 309 | *prgProperties = pProperties; | ||
| 310 | pProperties = NULL; | ||
| 311 | |||
| 312 | hr = S_OK; | ||
| 313 | |||
| 314 | LExit: | ||
| 315 | ReleaseNullObject(pixnNodes); | ||
| 316 | ReleaseMem(pProperties); | ||
| 317 | |||
| 318 | return hr; | ||
| 319 | } | ||
| 320 | |||
| 321 | extern "C" void MsiEnginePackageUninitialize( | ||
| 322 | __in BURN_PACKAGE* pPackage | ||
| 323 | ) | ||
| 324 | { | ||
| 325 | ReleaseStr(pPackage->Msi.sczProductCode); | ||
| 326 | ReleaseStr(pPackage->Msi.sczUpgradeCode); | ||
| 327 | ReleaseStr(pPackage->Msi.sczInstalledProductCode); | ||
| 328 | |||
| 329 | // free features | ||
| 330 | if (pPackage->Msi.rgFeatures) | ||
| 331 | { | ||
| 332 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 333 | { | ||
| 334 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 335 | |||
| 336 | ReleaseStr(pFeature->sczId); | ||
| 337 | ReleaseStr(pFeature->sczAddLocalCondition); | ||
| 338 | ReleaseStr(pFeature->sczAddSourceCondition); | ||
| 339 | ReleaseStr(pFeature->sczAdvertiseCondition); | ||
| 340 | ReleaseStr(pFeature->sczRollbackAddLocalCondition); | ||
| 341 | ReleaseStr(pFeature->sczRollbackAddSourceCondition); | ||
| 342 | ReleaseStr(pFeature->sczRollbackAdvertiseCondition); | ||
| 343 | } | ||
| 344 | MemFree(pPackage->Msi.rgFeatures); | ||
| 345 | } | ||
| 346 | |||
| 347 | // free properties | ||
| 348 | if (pPackage->Msi.rgProperties) | ||
| 349 | { | ||
| 350 | for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i) | ||
| 351 | { | ||
| 352 | BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i]; | ||
| 353 | |||
| 354 | ReleaseStr(pProperty->sczId); | ||
| 355 | ReleaseStr(pProperty->sczValue); | ||
| 356 | ReleaseStr(pProperty->sczRollbackValue); | ||
| 357 | ReleaseStr(pProperty->sczCondition); | ||
| 358 | } | ||
| 359 | MemFree(pPackage->Msi.rgProperties); | ||
| 360 | } | ||
| 361 | |||
| 362 | // free related MSIs | ||
| 363 | if (pPackage->Msi.rgRelatedMsis) | ||
| 364 | { | ||
| 365 | for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) | ||
| 366 | { | ||
| 367 | BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; | ||
| 368 | |||
| 369 | ReleaseStr(pRelatedMsi->sczUpgradeCode); | ||
| 370 | ReleaseMem(pRelatedMsi->rgdwLanguages); | ||
| 371 | } | ||
| 372 | MemFree(pPackage->Msi.rgRelatedMsis); | ||
| 373 | } | ||
| 374 | |||
| 375 | // free slipstream MSPs | ||
| 376 | if (pPackage->Msi.rgsczSlipstreamMspPackageIds) | ||
| 377 | { | ||
| 378 | for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 379 | { | ||
| 380 | ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]); | ||
| 381 | } | ||
| 382 | |||
| 383 | MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); | ||
| 384 | } | ||
| 385 | |||
| 386 | if (pPackage->Msi.rgpSlipstreamMspPackages) | ||
| 387 | { | ||
| 388 | MemFree(pPackage->Msi.rgpSlipstreamMspPackages); | ||
| 389 | } | ||
| 390 | |||
| 391 | // clear struct | ||
| 392 | memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); | ||
| 393 | } | ||
| 394 | |||
| 395 | extern "C" HRESULT MsiEngineDetectPackage( | ||
| 396 | __in BURN_PACKAGE* pPackage, | ||
| 397 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 398 | ) | ||
| 399 | { | ||
| 400 | Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); | ||
| 401 | |||
| 402 | HRESULT hr = S_OK; | ||
| 403 | LPWSTR sczInstalledVersion = NULL; | ||
| 404 | LPWSTR sczInstalledLanguage = NULL; | ||
| 405 | LPWSTR sczInstalledProductCode = NULL; | ||
| 406 | LPWSTR sczInstalledProviderKey = NULL; | ||
| 407 | INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; | ||
| 408 | BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; | ||
| 409 | BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; | ||
| 410 | WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; | ||
| 411 | DWORD64 qwVersion = 0; | ||
| 412 | UINT uLcid = 0; | ||
| 413 | BOOL fPerMachine = FALSE; | ||
| 414 | |||
| 415 | // detect self by product code | ||
| 416 | // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? | ||
| 417 | hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 418 | if (SUCCEEDED(hr)) | ||
| 419 | { | ||
| 420 | hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pPackage->Msi.qwInstalledVersion); | ||
| 421 | ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); | ||
| 422 | |||
| 423 | // compare versions | ||
| 424 | if (pPackage->Msi.qwVersion < pPackage->Msi.qwInstalledVersion) | ||
| 425 | { | ||
| 426 | operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; | ||
| 427 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; | ||
| 428 | } | ||
| 429 | else | ||
| 430 | { | ||
| 431 | if (pPackage->Msi.qwVersion > pPackage->Msi.qwInstalledVersion) | ||
| 432 | { | ||
| 433 | operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; | ||
| 434 | } | ||
| 435 | |||
| 436 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; | ||
| 437 | } | ||
| 438 | |||
| 439 | // Report related MSI package to BA. | ||
| 440 | if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation) | ||
| 441 | { | ||
| 442 | LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), LoggingVersionToString(pPackage->Msi.qwInstalledVersion), pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation)); | ||
| 443 | |||
| 444 | hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.qwInstalledVersion, operation); | ||
| 445 | ExitOnRootFailure(hr, "BA aborted detect related MSI package."); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. | ||
| 449 | { | ||
| 450 | // Check for newer, compatible packages based on a fixed provider key. | ||
| 451 | hr = DependencyDetectProviderKeyPackageId(pPackage, &sczInstalledProviderKey, &sczInstalledProductCode); | ||
| 452 | if (SUCCEEDED(hr)) | ||
| 453 | { | ||
| 454 | hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 455 | if (SUCCEEDED(hr)) | ||
| 456 | { | ||
| 457 | hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); | ||
| 458 | ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode); | ||
| 459 | |||
| 460 | if (pPackage->Msi.qwVersion < qwVersion) | ||
| 461 | { | ||
| 462 | LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode); | ||
| 463 | |||
| 464 | hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, qwVersion); | ||
| 465 | ExitOnRootFailure(hr, "BA aborted detect compatible MSI package."); | ||
| 466 | |||
| 467 | hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0); | ||
| 468 | ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package."); | ||
| 469 | |||
| 470 | pPackage->Msi.qwInstalledVersion = qwVersion; | ||
| 471 | pPackage->Msi.fCompatibleInstalled = TRUE; | ||
| 472 | } | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 477 | hr = S_OK; | ||
| 478 | } | ||
| 479 | else | ||
| 480 | { | ||
| 481 | ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode); | ||
| 482 | } | ||
| 483 | |||
| 484 | // detect related packages by upgrade code | ||
| 485 | for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) | ||
| 486 | { | ||
| 487 | BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; | ||
| 488 | |||
| 489 | for (DWORD iProduct = 0; ; ++iProduct) | ||
| 490 | { | ||
| 491 | // get product | ||
| 492 | hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode); | ||
| 493 | if (E_NOMOREITEMS == hr) | ||
| 494 | { | ||
| 495 | hr = S_OK; | ||
| 496 | break; | ||
| 497 | } | ||
| 498 | ExitOnFailure(hr, "Failed to enum related products."); | ||
| 499 | |||
| 500 | // If we found ourselves, skip because saying that a package is related to itself is nonsensical. | ||
| 501 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1)) | ||
| 502 | { | ||
| 503 | continue; | ||
| 504 | } | ||
| 505 | |||
| 506 | // get product version | ||
| 507 | hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 508 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) | ||
| 509 | { | ||
| 510 | ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); | ||
| 511 | fPerMachine = FALSE; | ||
| 512 | } | ||
| 513 | else | ||
| 514 | { | ||
| 515 | hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 516 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) | ||
| 517 | { | ||
| 518 | ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); | ||
| 519 | fPerMachine = TRUE; | ||
| 520 | } | ||
| 521 | else | ||
| 522 | { | ||
| 523 | hr = S_OK; | ||
| 524 | continue; | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion); | ||
| 529 | ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, wzProductCode); | ||
| 530 | |||
| 531 | // compare versions | ||
| 532 | if (pRelatedMsi->fMinProvided && (pRelatedMsi->fMinInclusive ? (qwVersion < pRelatedMsi->qwMinVersion) : (qwVersion <= pRelatedMsi->qwMinVersion))) | ||
| 533 | { | ||
| 534 | continue; | ||
| 535 | } | ||
| 536 | |||
| 537 | if (pRelatedMsi->fMaxProvided && (pRelatedMsi->fMaxInclusive ? (qwVersion > pRelatedMsi->qwMaxVersion) : (qwVersion >= pRelatedMsi->qwMaxVersion))) | ||
| 538 | { | ||
| 539 | continue; | ||
| 540 | } | ||
| 541 | |||
| 542 | // Filter by language if necessary. | ||
| 543 | uLcid = 0; // always reset the found language. | ||
| 544 | if (pRelatedMsi->cLanguages) | ||
| 545 | { | ||
| 546 | // If there is a language to get, convert it into an LCID. | ||
| 547 | hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage); | ||
| 548 | if (SUCCEEDED(hr)) | ||
| 549 | { | ||
| 550 | hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid); | ||
| 551 | } | ||
| 552 | |||
| 553 | // Ignore related product where we can't read the language. | ||
| 554 | if (FAILED(hr)) | ||
| 555 | { | ||
| 556 | LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL); | ||
| 557 | |||
| 558 | hr = S_OK; | ||
| 559 | continue; | ||
| 560 | } | ||
| 561 | |||
| 562 | BOOL fMatchedLcid = FALSE; | ||
| 563 | for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage) | ||
| 564 | { | ||
| 565 | if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage]) | ||
| 566 | { | ||
| 567 | fMatchedLcid = TRUE; | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | // Skip the product if the language did not meet the inclusive/exclusive criteria. | ||
| 573 | if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid)) | ||
| 574 | { | ||
| 575 | continue; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade | ||
| 580 | // would take place since that is the overwhelmingly common use of detect-only related packages. If | ||
| 581 | // not detect-only then it's easy; we're clearly doing a major upgrade. | ||
| 582 | if (pRelatedMsi->fOnlyDetect) | ||
| 583 | { | ||
| 584 | // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade | ||
| 585 | // or even something else. | ||
| 586 | if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) | ||
| 587 | { | ||
| 588 | relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; | ||
| 589 | } | ||
| 590 | // It can't be a downgrade if the upgrade codes aren't the same. | ||
| 591 | else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState && | ||
| 592 | pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1)) | ||
| 593 | { | ||
| 594 | relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; | ||
| 595 | operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; | ||
| 596 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; | ||
| 597 | } | ||
| 598 | else // we're already on the machine so the detect-only *must* be for detection purposes only. | ||
| 599 | { | ||
| 600 | relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | else | ||
| 604 | { | ||
| 605 | relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; | ||
| 606 | operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; | ||
| 607 | } | ||
| 608 | |||
| 609 | LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), LoggingVersionToString(qwVersion), uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); | ||
| 610 | |||
| 611 | // Pass to BA. | ||
| 612 | hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, qwVersion, relatedMsiOperation); | ||
| 613 | ExitOnRootFailure(hr, "BA aborted detect related MSI package."); | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | // detect features | ||
| 618 | if (pPackage->Msi.cFeatures) | ||
| 619 | { | ||
| 620 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 621 | { | ||
| 622 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 623 | |||
| 624 | // Try to detect features state if the product is present on the machine. | ||
| 625 | if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState) | ||
| 626 | { | ||
| 627 | hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); | ||
| 628 | ExitOnFailure(hr, "Failed to query feature state."); | ||
| 629 | |||
| 630 | if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed. | ||
| 631 | { | ||
| 632 | installState = INSTALLSTATE_ABSENT; | ||
| 633 | } | ||
| 634 | } | ||
| 635 | else // MSI not installed then the features can't be either. | ||
| 636 | { | ||
| 637 | installState = INSTALLSTATE_ABSENT; | ||
| 638 | } | ||
| 639 | |||
| 640 | // set current state | ||
| 641 | switch (installState) | ||
| 642 | { | ||
| 643 | case INSTALLSTATE_ABSENT: | ||
| 644 | pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; | ||
| 645 | break; | ||
| 646 | case INSTALLSTATE_ADVERTISED: | ||
| 647 | pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; | ||
| 648 | break; | ||
| 649 | case INSTALLSTATE_LOCAL: | ||
| 650 | pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; | ||
| 651 | break; | ||
| 652 | case INSTALLSTATE_SOURCE: | ||
| 653 | pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; | ||
| 654 | break; | ||
| 655 | default: | ||
| 656 | hr = E_UNEXPECTED; | ||
| 657 | ExitOnRootFailure(hr, "Invalid state value."); | ||
| 658 | } | ||
| 659 | |||
| 660 | // Pass to BA. | ||
| 661 | hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState); | ||
| 662 | ExitOnRootFailure(hr, "BA aborted detect MSI feature."); | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | LExit: | ||
| 667 | ReleaseStr(sczInstalledProviderKey); | ||
| 668 | ReleaseStr(sczInstalledProductCode); | ||
| 669 | ReleaseStr(sczInstalledLanguage); | ||
| 670 | ReleaseStr(sczInstalledVersion); | ||
| 671 | |||
| 672 | return hr; | ||
| 673 | } | ||
| 674 | |||
| 675 | // | ||
| 676 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
| 677 | // | ||
| 678 | extern "C" HRESULT MsiEnginePlanCalculatePackage( | ||
| 679 | __in BURN_PACKAGE* pPackage, | ||
| 680 | __in BURN_VARIABLES* pVariables, | ||
| 681 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 682 | __out BOOL* pfBARequestedCache | ||
| 683 | ) | ||
| 684 | { | ||
| 685 | Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); | ||
| 686 | |||
| 687 | HRESULT hr = S_OK; | ||
| 688 | DWORD64 qwVersion = pPackage->Msi.qwVersion; | ||
| 689 | DWORD64 qwInstalledVersion = pPackage->Msi.qwInstalledVersion; | ||
| 690 | BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 691 | BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 692 | BOOL fFeatureActionDelta = FALSE; | ||
| 693 | BOOL fRollbackFeatureActionDelta = FALSE; | ||
| 694 | BOOL fBARequestedCache = FALSE; | ||
| 695 | |||
| 696 | if (pPackage->Msi.cFeatures) | ||
| 697 | { | ||
| 698 | // If the package is present and we're repairing it. | ||
| 699 | BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); | ||
| 700 | |||
| 701 | LogId(REPORT_STANDARD, MSG_PLAN_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId); | ||
| 702 | |||
| 703 | // plan features | ||
| 704 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 705 | { | ||
| 706 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 707 | BOOTSTRAPPER_FEATURE_STATE defaultFeatureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; | ||
| 708 | BOOTSTRAPPER_FEATURE_STATE featureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; | ||
| 709 | BOOTSTRAPPER_FEATURE_STATE featureExpectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; | ||
| 710 | |||
| 711 | // Evaluate feature conditions. | ||
| 712 | hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &defaultFeatureRequestedState); | ||
| 713 | ExitOnFailure(hr, "Failed to evaluate requested state conditions."); | ||
| 714 | |||
| 715 | hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &featureExpectedState); | ||
| 716 | ExitOnFailure(hr, "Failed to evaluate expected state conditions."); | ||
| 717 | |||
| 718 | // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. | ||
| 719 | featureRequestedState = defaultFeatureRequestedState; | ||
| 720 | |||
| 721 | // Send plan MSI feature message to BA. | ||
| 722 | hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &featureRequestedState); | ||
| 723 | ExitOnRootFailure(hr, "BA aborted plan MSI feature."); | ||
| 724 | |||
| 725 | // Calculate feature actions. | ||
| 726 | hr = CalculateFeatureAction(pFeature->currentState, featureRequestedState, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); | ||
| 727 | ExitOnFailure(hr, "Failed to calculate execute feature state."); | ||
| 728 | |||
| 729 | hr = CalculateFeatureAction(featureRequestedState, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? featureExpectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); | ||
| 730 | ExitOnFailure(hr, "Failed to calculate rollback feature state."); | ||
| 731 | |||
| 732 | LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(defaultFeatureRequestedState), LoggingMsiFeatureStateToString(featureRequestedState), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback)); | ||
| 733 | } | ||
| 734 | } | ||
| 735 | |||
| 736 | // execute action | ||
| 737 | switch (pPackage->currentState) | ||
| 738 | { | ||
| 739 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; | ||
| 740 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 741 | if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) | ||
| 742 | { | ||
| 743 | // Take a look at the version and determine if this is a potential | ||
| 744 | // minor upgrade (same ProductCode newer ProductVersion), otherwise, | ||
| 745 | // there is a newer version so no work necessary. | ||
| 746 | if (qwVersion > qwInstalledVersion) | ||
| 747 | { | ||
| 748 | execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; | ||
| 749 | } | ||
| 750 | else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) | ||
| 751 | { | ||
| 752 | execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; | ||
| 753 | } | ||
| 754 | else | ||
| 755 | { | ||
| 756 | execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 757 | } | ||
| 758 | } | ||
| 759 | else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) && | ||
| 760 | pPackage->fUninstallable) // removing a package that can be removed. | ||
| 761 | { | ||
| 762 | execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; | ||
| 763 | } | ||
| 764 | else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested) | ||
| 765 | { | ||
| 766 | execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; | ||
| 767 | } | ||
| 768 | else | ||
| 769 | { | ||
| 770 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 771 | } | ||
| 772 | break; | ||
| 773 | |||
| 774 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 775 | switch (pPackage->requested) | ||
| 776 | { | ||
| 777 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 778 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 779 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 780 | break; | ||
| 781 | |||
| 782 | default: | ||
| 783 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 784 | break; | ||
| 785 | } | ||
| 786 | break; | ||
| 787 | |||
| 788 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; | ||
| 789 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 790 | switch (pPackage->requested) | ||
| 791 | { | ||
| 792 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 793 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 794 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 795 | break; | ||
| 796 | |||
| 797 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 798 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 799 | fBARequestedCache = TRUE; | ||
| 800 | break; | ||
| 801 | |||
| 802 | default: | ||
| 803 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 804 | break; | ||
| 805 | } | ||
| 806 | break; | ||
| 807 | |||
| 808 | default: | ||
| 809 | hr = E_INVALIDARG; | ||
| 810 | ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState); | ||
| 811 | } | ||
| 812 | |||
| 813 | // Calculate the rollback action if there is an execute action. | ||
| 814 | if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) | ||
| 815 | { | ||
| 816 | switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) | ||
| 817 | { | ||
| 818 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; | ||
| 819 | case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: | ||
| 820 | switch (pPackage->requested) | ||
| 821 | { | ||
| 822 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: | ||
| 823 | rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 824 | break; | ||
| 825 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 826 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 827 | break; | ||
| 828 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
| 829 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 830 | rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 831 | break; | ||
| 832 | default: | ||
| 833 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 834 | break; | ||
| 835 | } | ||
| 836 | break; | ||
| 837 | |||
| 838 | case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; | ||
| 839 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; | ||
| 840 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 841 | // If we requested to put the package on the machine then remove the package during rollback | ||
| 842 | // if the package is uninstallable. | ||
| 843 | if ((BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) && | ||
| 844 | pPackage->fUninstallable) | ||
| 845 | { | ||
| 846 | rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; | ||
| 847 | } | ||
| 848 | else | ||
| 849 | { | ||
| 850 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 851 | } | ||
| 852 | break; | ||
| 853 | |||
| 854 | default: | ||
| 855 | hr = E_INVALIDARG; | ||
| 856 | ExitOnRootFailure(hr, "Invalid package detection result encountered."); | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | // return values | ||
| 861 | pPackage->execute = execute; | ||
| 862 | pPackage->rollback = rollback; | ||
| 863 | |||
| 864 | if (pfBARequestedCache) | ||
| 865 | { | ||
| 866 | *pfBARequestedCache = fBARequestedCache; | ||
| 867 | } | ||
| 868 | |||
| 869 | LExit: | ||
| 870 | return hr; | ||
| 871 | } | ||
| 872 | |||
| 873 | // | ||
| 874 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
| 875 | // | ||
| 876 | extern "C" HRESULT MsiEnginePlanAddPackage( | ||
| 877 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 878 | __in BURN_PACKAGE* pPackage, | ||
| 879 | __in BURN_PLAN* pPlan, | ||
| 880 | __in BURN_LOGGING* pLog, | ||
| 881 | __in BURN_VARIABLES* pVariables, | ||
| 882 | __in_opt HANDLE hCacheEvent, | ||
| 883 | __in BOOL fPlanPackageCacheRollback | ||
| 884 | ) | ||
| 885 | { | ||
| 886 | HRESULT hr = S_OK; | ||
| 887 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 888 | BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL; | ||
| 889 | BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL; | ||
| 890 | |||
| 891 | if (pPackage->Msi.cFeatures) | ||
| 892 | { | ||
| 893 | // Allocate and populate array for feature actions. | ||
| 894 | rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); | ||
| 895 | ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); | ||
| 896 | |||
| 897 | rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); | ||
| 898 | ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions."); | ||
| 899 | |||
| 900 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 901 | { | ||
| 902 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 903 | |||
| 904 | // calculate feature actions | ||
| 905 | rgFeatureActions[i] = pFeature->execute; | ||
| 906 | rgRollbackFeatureActions[i] = pFeature->rollback; | ||
| 907 | } | ||
| 908 | } | ||
| 909 | |||
| 910 | // add wait for cache | ||
| 911 | if (hCacheEvent) | ||
| 912 | { | ||
| 913 | hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); | ||
| 914 | ExitOnFailure(hr, "Failed to plan package cache syncpoint"); | ||
| 915 | } | ||
| 916 | |||
| 917 | hr = DependencyPlanPackage(NULL, pPackage, pPlan); | ||
| 918 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
| 919 | |||
| 920 | // add rollback action | ||
| 921 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) | ||
| 922 | { | ||
| 923 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 924 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 925 | |||
| 926 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; | ||
| 927 | pAction->msiPackage.pPackage = pPackage; | ||
| 928 | pAction->msiPackage.action = pPackage->rollback; | ||
| 929 | pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); | ||
| 930 | pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; | ||
| 931 | rgRollbackFeatureActions = NULL; | ||
| 932 | |||
| 933 | LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. | ||
| 934 | pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; | ||
| 935 | |||
| 936 | // Plan a checkpoint between rollback and execute so that we always attempt | ||
| 937 | // rollback in the case that the MSI was not able to rollback itself (e.g. | ||
| 938 | // user pushes cancel after InstallFinalize). | ||
| 939 | hr = PlanExecuteCheckpoint(pPlan); | ||
| 940 | ExitOnFailure(hr, "Failed to append execute checkpoint."); | ||
| 941 | } | ||
| 942 | |||
| 943 | // add execute action | ||
| 944 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) | ||
| 945 | { | ||
| 946 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 947 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 948 | |||
| 949 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; | ||
| 950 | pAction->msiPackage.pPackage = pPackage; | ||
| 951 | pAction->msiPackage.action = pPackage->execute; | ||
| 952 | pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action); | ||
| 953 | pAction->msiPackage.rgFeatures = rgFeatureActions; | ||
| 954 | rgFeatureActions = NULL; | ||
| 955 | |||
| 956 | LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. | ||
| 957 | pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; | ||
| 958 | } | ||
| 959 | |||
| 960 | // Update any slipstream patches' state. | ||
| 961 | for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 962 | { | ||
| 963 | BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; | ||
| 964 | AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); | ||
| 965 | |||
| 966 | MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback); | ||
| 967 | } | ||
| 968 | |||
| 969 | LExit: | ||
| 970 | ReleaseMem(rgFeatureActions); | ||
| 971 | ReleaseMem(rgRollbackFeatureActions); | ||
| 972 | |||
| 973 | return hr; | ||
| 974 | } | ||
| 975 | |||
| 976 | extern "C" HRESULT MsiEngineAddCompatiblePackage( | ||
| 977 | __in BURN_PACKAGES* pPackages, | ||
| 978 | __in const BURN_PACKAGE* pPackage, | ||
| 979 | __out_opt BURN_PACKAGE** ppCompatiblePackage | ||
| 980 | ) | ||
| 981 | { | ||
| 982 | Assert(BURN_PACKAGE_TYPE_MSI == pPackage->type); | ||
| 983 | |||
| 984 | HRESULT hr = S_OK; | ||
| 985 | BURN_PACKAGE* pCompatiblePackage = NULL; | ||
| 986 | LPWSTR sczInstalledVersion = NULL; | ||
| 987 | |||
| 988 | // Allocate enough memory all at once so pointers to packages within | ||
| 989 | // aren't invalidated if we otherwise reallocated. | ||
| 990 | hr = PackageEnsureCompatiblePackagesArray(pPackages); | ||
| 991 | ExitOnFailure(hr, "Failed to allocate memory for compatible MSI package."); | ||
| 992 | |||
| 993 | pCompatiblePackage = pPackages->rgCompatiblePackages + pPackages->cCompatiblePackages; | ||
| 994 | ++pPackages->cCompatiblePackages; | ||
| 995 | |||
| 996 | pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; | ||
| 997 | |||
| 998 | // Read in the compatible ProductCode if not already available. | ||
| 999 | if (pPackage->Msi.sczInstalledProductCode) | ||
| 1000 | { | ||
| 1001 | hr = StrAllocString(&pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczInstalledProductCode, 0); | ||
| 1002 | ExitOnFailure(hr, "Failed to copy installed ProductCode to compatible package."); | ||
| 1003 | } | ||
| 1004 | else | ||
| 1005 | { | ||
| 1006 | hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, &pCompatiblePackage->Msi.sczProductCode); | ||
| 1007 | ExitOnFailure(hr, "Failed to detect compatible package from provider key."); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | // Read in the compatible ProductVersion if not already available. | ||
| 1011 | if (pPackage->Msi.qwInstalledVersion) | ||
| 1012 | { | ||
| 1013 | pCompatiblePackage->Msi.qwVersion = pPackage->Msi.qwInstalledVersion; | ||
| 1014 | |||
| 1015 | hr = FileVersionToStringEx(pCompatiblePackage->Msi.qwVersion, &sczInstalledVersion); | ||
| 1016 | ExitOnFailure(hr, "Failed to format version number string."); | ||
| 1017 | } | ||
| 1018 | else | ||
| 1019 | { | ||
| 1020 | hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 1021 | ExitOnFailure(hr, "Failed to read version from compatible package."); | ||
| 1022 | |||
| 1023 | hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pCompatiblePackage->Msi.qwVersion); | ||
| 1024 | ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | // For now, copy enough information to support uninstalling the newer, compatible package. | ||
| 1028 | hr = StrAllocString(&pCompatiblePackage->sczId, pCompatiblePackage->Msi.sczProductCode, 0); | ||
| 1029 | ExitOnFailure(hr, "Failed to copy installed ProductCode as compatible package ID."); | ||
| 1030 | |||
| 1031 | pCompatiblePackage->fPerMachine = pPackage->fPerMachine; | ||
| 1032 | pCompatiblePackage->fUninstallable = pPackage->fUninstallable; | ||
| 1033 | pCompatiblePackage->cacheType = pPackage->cacheType; | ||
| 1034 | |||
| 1035 | // Removing compatible packages is best effort. | ||
| 1036 | pCompatiblePackage->fVital = FALSE; | ||
| 1037 | |||
| 1038 | // Format a suitable log path variable from the original package. | ||
| 1039 | hr = StrAllocFormatted(&pCompatiblePackage->sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable); | ||
| 1040 | ExitOnFailure(hr, "Failed to format log path variable for compatible package."); | ||
| 1041 | |||
| 1042 | // Use the default cache ID generation from the binder. | ||
| 1043 | hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, sczInstalledVersion); | ||
| 1044 | ExitOnFailure(hr, "Failed to format cache ID for compatible package."); | ||
| 1045 | |||
| 1046 | pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; | ||
| 1047 | pCompatiblePackage->cache = BURN_CACHE_STATE_PARTIAL; // Cannot know if it's complete or not. | ||
| 1048 | |||
| 1049 | // Copy all the providers to ensure no dependents. | ||
| 1050 | if (pPackage->cDependencyProviders) | ||
| 1051 | { | ||
| 1052 | pCompatiblePackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * pPackage->cDependencyProviders, TRUE); | ||
| 1053 | ExitOnNull(pCompatiblePackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate for compatible package providers."); | ||
| 1054 | |||
| 1055 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 1056 | { | ||
| 1057 | BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; | ||
| 1058 | BURN_DEPENDENCY_PROVIDER* pCompatibleProvider = pCompatiblePackage->rgDependencyProviders + i; | ||
| 1059 | |||
| 1060 | // Only need to copy the key for uninstall. | ||
| 1061 | hr = StrAllocString(&pCompatibleProvider->sczKey, pProvider->sczKey, 0); | ||
| 1062 | ExitOnFailure(hr, "Failed to copy the compatible provider key."); | ||
| 1063 | |||
| 1064 | // Assume the package version is the same as the provider version. | ||
| 1065 | hr = StrAllocString(&pCompatibleProvider->sczVersion, sczInstalledVersion, 0); | ||
| 1066 | ExitOnFailure(hr, "Failed to copy the compatible provider version."); | ||
| 1067 | |||
| 1068 | // Assume provider keys are similarly authored for this package. | ||
| 1069 | pCompatibleProvider->fImported = pProvider->fImported; | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | pCompatiblePackage->cDependencyProviders = pPackage->cDependencyProviders; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI; | ||
| 1076 | pCompatiblePackage->Msi.fDisplayInternalUI = pPackage->Msi.fDisplayInternalUI; | ||
| 1077 | |||
| 1078 | if (ppCompatiblePackage) | ||
| 1079 | { | ||
| 1080 | *ppCompatiblePackage = pCompatiblePackage; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | LExit: | ||
| 1084 | ReleaseStr(sczInstalledVersion); | ||
| 1085 | |||
| 1086 | return hr; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | extern "C" HRESULT MsiEngineExecutePackage( | ||
| 1090 | __in_opt HWND hwndParent, | ||
| 1091 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 1092 | __in BURN_VARIABLES* pVariables, | ||
| 1093 | __in BOOL fRollback, | ||
| 1094 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 1095 | __in LPVOID pvContext, | ||
| 1096 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 1097 | ) | ||
| 1098 | { | ||
| 1099 | HRESULT hr = S_OK; | ||
| 1100 | WIU_MSI_EXECUTE_CONTEXT context = { }; | ||
| 1101 | WIU_RESTART restart = WIU_RESTART_NONE; | ||
| 1102 | |||
| 1103 | LPWSTR sczInstalledVersion = NULL; | ||
| 1104 | LPWSTR sczCachedDirectory = NULL; | ||
| 1105 | LPWSTR sczMsiPath = NULL; | ||
| 1106 | LPWSTR sczProperties = NULL; | ||
| 1107 | LPWSTR sczObfuscatedProperties = NULL; | ||
| 1108 | |||
| 1109 | // During rollback, if the package is already in the rollback state we expect don't | ||
| 1110 | // touch it again. | ||
| 1111 | if (fRollback) | ||
| 1112 | { | ||
| 1113 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) | ||
| 1114 | { | ||
| 1115 | hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 1116 | if (FAILED(hr)) // package not present. | ||
| 1117 | { | ||
| 1118 | LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); | ||
| 1119 | |||
| 1120 | hr = S_OK; | ||
| 1121 | ExitFunction(); | ||
| 1122 | } | ||
| 1123 | } | ||
| 1124 | else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) | ||
| 1125 | { | ||
| 1126 | hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); | ||
| 1127 | if (SUCCEEDED(hr)) // package present. | ||
| 1128 | { | ||
| 1129 | LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); | ||
| 1130 | |||
| 1131 | hr = S_OK; | ||
| 1132 | ExitFunction(); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | hr = S_OK; | ||
| 1136 | } | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | // Default to "verbose" logging and set extra debug mode only if explicitly required. | ||
| 1140 | DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; | ||
| 1141 | |||
| 1142 | if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) | ||
| 1143 | { | ||
| 1144 | dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) | ||
| 1148 | { | ||
| 1149 | // get cached MSI path | ||
| 1150 | hr = CacheGetCompletedPath(pExecuteAction->msiPackage.pPackage->fPerMachine, pExecuteAction->msiPackage.pPackage->sczCacheId, &sczCachedDirectory); | ||
| 1151 | ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId); | ||
| 1152 | |||
| 1153 | // Best effort to set the execute package cache folder variable. | ||
| 1154 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); | ||
| 1155 | |||
| 1156 | hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath); | ||
| 1157 | ExitOnFailure(hr, "Failed to build MSI path."); | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | // Best effort to set the execute package action variable. | ||
| 1161 | VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); | ||
| 1162 | |||
| 1163 | // Wire up the external UI handler and logging. | ||
| 1164 | hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); | ||
| 1165 | ExitOnFailure(hr, "Failed to initialize external UI handler."); | ||
| 1166 | |||
| 1167 | if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) | ||
| 1168 | { | ||
| 1169 | hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0); | ||
| 1170 | ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | // set up properties | ||
| 1174 | hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); | ||
| 1175 | ExitOnFailure(hr, "Failed to add properties to argument string."); | ||
| 1176 | |||
| 1177 | hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); | ||
| 1178 | ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); | ||
| 1179 | |||
| 1180 | // add feature action properties | ||
| 1181 | hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); | ||
| 1182 | ExitOnFailure(hr, "Failed to add feature action properties to argument string."); | ||
| 1183 | |||
| 1184 | hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); | ||
| 1185 | ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); | ||
| 1186 | |||
| 1187 | // add slipstream patch properties | ||
| 1188 | hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties); | ||
| 1189 | ExitOnFailure(hr, "Failed to add patch properties to argument string."); | ||
| 1190 | |||
| 1191 | hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties); | ||
| 1192 | ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); | ||
| 1193 | |||
| 1194 | LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); | ||
| 1195 | |||
| 1196 | // | ||
| 1197 | // Do the actual action. | ||
| 1198 | // | ||
| 1199 | switch (pExecuteAction->msiPackage.action) | ||
| 1200 | { | ||
| 1201 | case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL: | ||
| 1202 | hr = StrAllocConcatSecure(&sczProperties, L" ACTION=ADMIN", 0); | ||
| 1203 | ExitOnFailure(hr, "Failed to add ADMIN property on admin install."); | ||
| 1204 | __fallthrough; | ||
| 1205 | |||
| 1206 | case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; | ||
| 1207 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
| 1208 | hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); | ||
| 1209 | ExitOnFailure(hr, "Failed to add reboot suppression property on install."); | ||
| 1210 | |||
| 1211 | hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); | ||
| 1212 | ExitOnFailure(hr, "Failed to install MSI package."); | ||
| 1213 | |||
| 1214 | RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); | ||
| 1215 | break; | ||
| 1216 | |||
| 1217 | case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: | ||
| 1218 | // If feature selection is not enabled, then reinstall the existing features to ensure they get | ||
| 1219 | // updated. | ||
| 1220 | if (0 == pExecuteAction->msiPackage.pPackage->Msi.cFeatures) | ||
| 1221 | { | ||
| 1222 | hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0); | ||
| 1223 | ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade."); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0); | ||
| 1227 | ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade."); | ||
| 1228 | |||
| 1229 | hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); | ||
| 1230 | ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); | ||
| 1231 | |||
| 1232 | RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath); | ||
| 1233 | break; | ||
| 1234 | |||
| 1235 | case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; | ||
| 1236 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | ||
| 1237 | { | ||
| 1238 | LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || | ||
| 1239 | pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; | ||
| 1240 | LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e"; | ||
| 1241 | |||
| 1242 | hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); | ||
| 1243 | ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Ignore all dependencies, since the Burn engine already performed the check. | ||
| 1247 | hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); | ||
| 1248 | ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); | ||
| 1249 | |||
| 1250 | hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); | ||
| 1251 | ExitOnFailure(hr, "Failed to run maintenance mode for MSI package."); | ||
| 1252 | break; | ||
| 1253 | |||
| 1254 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 1255 | hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); | ||
| 1256 | ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); | ||
| 1257 | |||
| 1258 | // Ignore all dependencies, since the Burn engine already performed the check. | ||
| 1259 | hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); | ||
| 1260 | ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); | ||
| 1261 | |||
| 1262 | hr = WiuConfigureProductEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); | ||
| 1263 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) | ||
| 1264 | { | ||
| 1265 | LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId); | ||
| 1266 | hr = S_OK; | ||
| 1267 | } | ||
| 1268 | ExitOnFailure(hr, "Failed to uninstall MSI package."); | ||
| 1269 | break; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | LExit: | ||
| 1273 | WiuUninitializeExternalUI(&context); | ||
| 1274 | |||
| 1275 | StrSecureZeroFreeString(sczProperties); | ||
| 1276 | ReleaseStr(sczObfuscatedProperties); | ||
| 1277 | ReleaseStr(sczMsiPath); | ||
| 1278 | ReleaseStr(sczCachedDirectory); | ||
| 1279 | ReleaseStr(sczInstalledVersion); | ||
| 1280 | |||
| 1281 | switch (restart) | ||
| 1282 | { | ||
| 1283 | case WIU_RESTART_NONE: | ||
| 1284 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 1285 | break; | ||
| 1286 | |||
| 1287 | case WIU_RESTART_REQUIRED: | ||
| 1288 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 1289 | break; | ||
| 1290 | |||
| 1291 | case WIU_RESTART_INITIATED: | ||
| 1292 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
| 1293 | break; | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | // Best effort to clear the execute package cache folder and action variables. | ||
| 1297 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); | ||
| 1298 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); | ||
| 1299 | |||
| 1300 | return hr; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | // The contents of psczProperties may be sensitive, should keep encrypted and SecureZeroFree. | ||
| 1304 | extern "C" HRESULT MsiEngineConcatProperties( | ||
| 1305 | __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, | ||
| 1306 | __in DWORD cProperties, | ||
| 1307 | __in BURN_VARIABLES* pVariables, | ||
| 1308 | __in BOOL fRollback, | ||
| 1309 | __deref_out_z LPWSTR* psczProperties, | ||
| 1310 | __in BOOL fObfuscateHiddenVariables | ||
| 1311 | ) | ||
| 1312 | { | ||
| 1313 | HRESULT hr = S_OK; | ||
| 1314 | LPWSTR sczValue = NULL; | ||
| 1315 | LPWSTR sczEscapedValue = NULL; | ||
| 1316 | LPWSTR sczProperty = NULL; | ||
| 1317 | |||
| 1318 | for (DWORD i = 0; i < cProperties; ++i) | ||
| 1319 | { | ||
| 1320 | BURN_MSIPROPERTY* pProperty = &rgProperties[i]; | ||
| 1321 | |||
| 1322 | if (pProperty->sczCondition && *pProperty->sczCondition) | ||
| 1323 | { | ||
| 1324 | BOOL fCondition = FALSE; | ||
| 1325 | |||
| 1326 | hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition); | ||
| 1327 | if (FAILED(hr) || !fCondition) | ||
| 1328 | { | ||
| 1329 | LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition)); | ||
| 1330 | continue; | ||
| 1331 | } | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | // format property value | ||
| 1335 | if (fObfuscateHiddenVariables) | ||
| 1336 | { | ||
| 1337 | hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); | ||
| 1338 | } | ||
| 1339 | else | ||
| 1340 | { | ||
| 1341 | hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); | ||
| 1342 | ExitOnFailure(hr, "Failed to format property value."); | ||
| 1343 | } | ||
| 1344 | ExitOnFailure(hr, "Failed to format property value."); | ||
| 1345 | |||
| 1346 | // escape property value | ||
| 1347 | hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables); | ||
| 1348 | ExitOnFailure(hr, "Failed to escape string."); | ||
| 1349 | |||
| 1350 | // build part | ||
| 1351 | hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue); | ||
| 1352 | ExitOnFailure(hr, "Failed to format property string part."); | ||
| 1353 | |||
| 1354 | // append to property string | ||
| 1355 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0); | ||
| 1356 | ExitOnFailure(hr, "Failed to append property string part."); | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | LExit: | ||
| 1360 | StrSecureZeroFreeString(sczValue); | ||
| 1361 | StrSecureZeroFreeString(sczEscapedValue); | ||
| 1362 | StrSecureZeroFreeString(sczProperty); | ||
| 1363 | return hr; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | extern "C" INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( | ||
| 1367 | __in BOOL fDisplayInternalUI, | ||
| 1368 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 1369 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
| 1370 | ) | ||
| 1371 | { | ||
| 1372 | // Assume there will be no internal UI displayed. | ||
| 1373 | INSTALLUILEVEL uiLevel = static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); | ||
| 1374 | |||
| 1375 | // suppress internal UI during uninstall to mimic ARP and "msiexec /x" behavior | ||
| 1376 | if (fDisplayInternalUI && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != actionState && BOOTSTRAPPER_ACTION_STATE_REPAIR != actionState) | ||
| 1377 | { | ||
| 1378 | switch (display) | ||
| 1379 | { | ||
| 1380 | case BOOTSTRAPPER_DISPLAY_FULL: | ||
| 1381 | uiLevel = INSTALLUILEVEL_FULL; | ||
| 1382 | break; | ||
| 1383 | |||
| 1384 | case BOOTSTRAPPER_DISPLAY_PASSIVE: | ||
| 1385 | uiLevel = INSTALLUILEVEL_REDUCED; | ||
| 1386 | break; | ||
| 1387 | } | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | return uiLevel; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | |||
| 1394 | // internal helper functions | ||
| 1395 | |||
| 1396 | static HRESULT ParseRelatedMsiFromXml( | ||
| 1397 | __in IXMLDOMNode* pixnRelatedMsi, | ||
| 1398 | __in BURN_RELATED_MSI* pRelatedMsi | ||
| 1399 | ) | ||
| 1400 | { | ||
| 1401 | HRESULT hr = S_OK; | ||
| 1402 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 1403 | IXMLDOMNode* pixnNode = NULL; | ||
| 1404 | DWORD cNodes = 0; | ||
| 1405 | LPWSTR scz = NULL; | ||
| 1406 | |||
| 1407 | // @Id | ||
| 1408 | hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode); | ||
| 1409 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 1410 | |||
| 1411 | // @MinVersion | ||
| 1412 | hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz); | ||
| 1413 | if (E_NOTFOUND != hr) | ||
| 1414 | { | ||
| 1415 | ExitOnFailure(hr, "Failed to get @MinVersion."); | ||
| 1416 | |||
| 1417 | hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMinVersion); | ||
| 1418 | ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); | ||
| 1419 | |||
| 1420 | // flag that we have a min version | ||
| 1421 | pRelatedMsi->fMinProvided = TRUE; | ||
| 1422 | |||
| 1423 | // @MinInclusive | ||
| 1424 | hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive); | ||
| 1425 | ExitOnFailure(hr, "Failed to get @MinInclusive."); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | // @MaxVersion | ||
| 1429 | hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz); | ||
| 1430 | if (E_NOTFOUND != hr) | ||
| 1431 | { | ||
| 1432 | ExitOnFailure(hr, "Failed to get @MaxVersion."); | ||
| 1433 | |||
| 1434 | hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMaxVersion); | ||
| 1435 | ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); | ||
| 1436 | |||
| 1437 | // flag that we have a max version | ||
| 1438 | pRelatedMsi->fMaxProvided = TRUE; | ||
| 1439 | |||
| 1440 | // @MaxInclusive | ||
| 1441 | hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive); | ||
| 1442 | ExitOnFailure(hr, "Failed to get @MaxInclusive."); | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | // @OnlyDetect | ||
| 1446 | hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect); | ||
| 1447 | ExitOnFailure(hr, "Failed to get @OnlyDetect."); | ||
| 1448 | |||
| 1449 | // select language nodes | ||
| 1450 | hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes); | ||
| 1451 | ExitOnFailure(hr, "Failed to select language nodes."); | ||
| 1452 | |||
| 1453 | // get language node count | ||
| 1454 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 1455 | ExitOnFailure(hr, "Failed to get language node count."); | ||
| 1456 | |||
| 1457 | if (cNodes) | ||
| 1458 | { | ||
| 1459 | // @LangInclusive | ||
| 1460 | hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive); | ||
| 1461 | ExitOnFailure(hr, "Failed to get @LangInclusive."); | ||
| 1462 | |||
| 1463 | // allocate memory for language IDs | ||
| 1464 | pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE); | ||
| 1465 | ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs."); | ||
| 1466 | |||
| 1467 | pRelatedMsi->cLanguages = cNodes; | ||
| 1468 | |||
| 1469 | // parse language elements | ||
| 1470 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 1471 | { | ||
| 1472 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 1473 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 1474 | |||
| 1475 | // @Id | ||
| 1476 | hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]); | ||
| 1477 | ExitOnFailure(hr, "Failed to get Language/@Id."); | ||
| 1478 | |||
| 1479 | // prepare next iteration | ||
| 1480 | ReleaseNullObject(pixnNode); | ||
| 1481 | } | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | hr = S_OK; | ||
| 1485 | |||
| 1486 | LExit: | ||
| 1487 | ReleaseObject(pixnNodes); | ||
| 1488 | ReleaseObject(pixnNode); | ||
| 1489 | ReleaseStr(scz); | ||
| 1490 | |||
| 1491 | return hr; | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | static HRESULT EvaluateActionStateConditions( | ||
| 1495 | __in BURN_VARIABLES* pVariables, | ||
| 1496 | __in_z_opt LPCWSTR sczAddLocalCondition, | ||
| 1497 | __in_z_opt LPCWSTR sczAddSourceCondition, | ||
| 1498 | __in_z_opt LPCWSTR sczAdvertiseCondition, | ||
| 1499 | __out BOOTSTRAPPER_FEATURE_STATE* pState | ||
| 1500 | ) | ||
| 1501 | { | ||
| 1502 | HRESULT hr = S_OK; | ||
| 1503 | BOOL fCondition = FALSE; | ||
| 1504 | |||
| 1505 | // if no condition was set, return no feature state | ||
| 1506 | if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition) | ||
| 1507 | { | ||
| 1508 | *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; | ||
| 1509 | ExitFunction(); | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | if (sczAddLocalCondition) | ||
| 1513 | { | ||
| 1514 | hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition); | ||
| 1515 | ExitOnFailure(hr, "Failed to evaluate add local condition."); | ||
| 1516 | |||
| 1517 | if (fCondition) | ||
| 1518 | { | ||
| 1519 | *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; | ||
| 1520 | ExitFunction(); | ||
| 1521 | } | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | if (sczAddSourceCondition) | ||
| 1525 | { | ||
| 1526 | hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition); | ||
| 1527 | ExitOnFailure(hr, "Failed to evaluate add source condition."); | ||
| 1528 | |||
| 1529 | if (fCondition) | ||
| 1530 | { | ||
| 1531 | *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; | ||
| 1532 | ExitFunction(); | ||
| 1533 | } | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | if (sczAdvertiseCondition) | ||
| 1537 | { | ||
| 1538 | hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition); | ||
| 1539 | ExitOnFailure(hr, "Failed to evaluate advertise condition."); | ||
| 1540 | |||
| 1541 | if (fCondition) | ||
| 1542 | { | ||
| 1543 | *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; | ||
| 1544 | ExitFunction(); | ||
| 1545 | } | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | // if no condition was true, set to absent | ||
| 1549 | *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; | ||
| 1550 | |||
| 1551 | LExit: | ||
| 1552 | return hr; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | static HRESULT CalculateFeatureAction( | ||
| 1556 | __in BOOTSTRAPPER_FEATURE_STATE currentState, | ||
| 1557 | __in BOOTSTRAPPER_FEATURE_STATE requestedState, | ||
| 1558 | __in BOOL fRepair, | ||
| 1559 | __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, | ||
| 1560 | __inout BOOL* pfDelta | ||
| 1561 | ) | ||
| 1562 | { | ||
| 1563 | HRESULT hr = S_OK; | ||
| 1564 | |||
| 1565 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; | ||
| 1566 | switch (requestedState) | ||
| 1567 | { | ||
| 1568 | case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: | ||
| 1569 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; | ||
| 1570 | break; | ||
| 1571 | |||
| 1572 | case BOOTSTRAPPER_FEATURE_STATE_ABSENT: | ||
| 1573 | if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState) | ||
| 1574 | { | ||
| 1575 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE; | ||
| 1576 | } | ||
| 1577 | break; | ||
| 1578 | |||
| 1579 | case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: | ||
| 1580 | if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState) | ||
| 1581 | { | ||
| 1582 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE; | ||
| 1583 | } | ||
| 1584 | else if (fRepair) | ||
| 1585 | { | ||
| 1586 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; | ||
| 1587 | } | ||
| 1588 | break; | ||
| 1589 | |||
| 1590 | case BOOTSTRAPPER_FEATURE_STATE_LOCAL: | ||
| 1591 | if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState) | ||
| 1592 | { | ||
| 1593 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL; | ||
| 1594 | } | ||
| 1595 | else if (fRepair) | ||
| 1596 | { | ||
| 1597 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; | ||
| 1598 | } | ||
| 1599 | break; | ||
| 1600 | |||
| 1601 | case BOOTSTRAPPER_FEATURE_STATE_SOURCE: | ||
| 1602 | if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState) | ||
| 1603 | { | ||
| 1604 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE; | ||
| 1605 | } | ||
| 1606 | else if (fRepair) | ||
| 1607 | { | ||
| 1608 | *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; | ||
| 1609 | } | ||
| 1610 | break; | ||
| 1611 | |||
| 1612 | default: | ||
| 1613 | hr = E_UNEXPECTED; | ||
| 1614 | ExitOnRootFailure(hr, "Invalid state value."); | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction) | ||
| 1618 | { | ||
| 1619 | *pfDelta = TRUE; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | LExit: | ||
| 1623 | return hr; | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | static HRESULT EscapePropertyArgumentString( | ||
| 1627 | __in LPCWSTR wzProperty, | ||
| 1628 | __inout_z LPWSTR* psczEscapedValue, | ||
| 1629 | __in BOOL fZeroOnRealloc | ||
| 1630 | ) | ||
| 1631 | { | ||
| 1632 | HRESULT hr = S_OK; | ||
| 1633 | DWORD cch = 0; | ||
| 1634 | DWORD cchEscape = 0; | ||
| 1635 | LPCWSTR wzSource = NULL; | ||
| 1636 | LPWSTR wzTarget = NULL; | ||
| 1637 | |||
| 1638 | // count characters to escape | ||
| 1639 | wzSource = wzProperty; | ||
| 1640 | while (*wzSource) | ||
| 1641 | { | ||
| 1642 | ++cch; | ||
| 1643 | if (L'\"' == *wzSource) | ||
| 1644 | { | ||
| 1645 | ++cchEscape; | ||
| 1646 | } | ||
| 1647 | ++wzSource; | ||
| 1648 | } | ||
| 1649 | |||
| 1650 | // allocate target buffer | ||
| 1651 | hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator | ||
| 1652 | ExitOnFailure(hr, "Failed to allocate string buffer."); | ||
| 1653 | |||
| 1654 | // write to target buffer | ||
| 1655 | wzSource = wzProperty; | ||
| 1656 | wzTarget = *psczEscapedValue; | ||
| 1657 | while (*wzSource) | ||
| 1658 | { | ||
| 1659 | *wzTarget = *wzSource; | ||
| 1660 | if (L'\"' == *wzTarget) | ||
| 1661 | { | ||
| 1662 | ++wzTarget; | ||
| 1663 | *wzTarget = L'\"'; | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | ++wzSource; | ||
| 1667 | ++wzTarget; | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | *wzTarget = L'\0'; // add null terminator | ||
| 1671 | |||
| 1672 | LExit: | ||
| 1673 | return hr; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | static HRESULT ConcatFeatureActionProperties( | ||
| 1677 | __in BURN_PACKAGE* pPackage, | ||
| 1678 | __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, | ||
| 1679 | __inout_z LPWSTR* psczArguments | ||
| 1680 | ) | ||
| 1681 | { | ||
| 1682 | HRESULT hr = S_OK; | ||
| 1683 | LPWSTR scz = NULL; | ||
| 1684 | LPWSTR sczAddLocal = NULL; | ||
| 1685 | LPWSTR sczAddSource = NULL; | ||
| 1686 | LPWSTR sczAddDefault = NULL; | ||
| 1687 | LPWSTR sczReinstall = NULL; | ||
| 1688 | LPWSTR sczAdvertise = NULL; | ||
| 1689 | LPWSTR sczRemove = NULL; | ||
| 1690 | |||
| 1691 | // features | ||
| 1692 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 1693 | { | ||
| 1694 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 1695 | |||
| 1696 | switch (rgFeatureActions[i]) | ||
| 1697 | { | ||
| 1698 | case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: | ||
| 1699 | if (sczAddLocal) | ||
| 1700 | { | ||
| 1701 | hr = StrAllocConcat(&sczAddLocal, L",", 0); | ||
| 1702 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1703 | } | ||
| 1704 | hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0); | ||
| 1705 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1706 | break; | ||
| 1707 | |||
| 1708 | case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: | ||
| 1709 | if (sczAddSource) | ||
| 1710 | { | ||
| 1711 | hr = StrAllocConcat(&sczAddSource, L",", 0); | ||
| 1712 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1713 | } | ||
| 1714 | hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0); | ||
| 1715 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1716 | break; | ||
| 1717 | |||
| 1718 | case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: | ||
| 1719 | if (sczAddDefault) | ||
| 1720 | { | ||
| 1721 | hr = StrAllocConcat(&sczAddDefault, L",", 0); | ||
| 1722 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1723 | } | ||
| 1724 | hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0); | ||
| 1725 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1726 | break; | ||
| 1727 | |||
| 1728 | case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: | ||
| 1729 | if (sczReinstall) | ||
| 1730 | { | ||
| 1731 | hr = StrAllocConcat(&sczReinstall, L",", 0); | ||
| 1732 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1733 | } | ||
| 1734 | hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0); | ||
| 1735 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1736 | break; | ||
| 1737 | |||
| 1738 | case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: | ||
| 1739 | if (sczAdvertise) | ||
| 1740 | { | ||
| 1741 | hr = StrAllocConcat(&sczAdvertise, L",", 0); | ||
| 1742 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1743 | } | ||
| 1744 | hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0); | ||
| 1745 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1746 | break; | ||
| 1747 | |||
| 1748 | case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: | ||
| 1749 | if (sczRemove) | ||
| 1750 | { | ||
| 1751 | hr = StrAllocConcat(&sczRemove, L",", 0); | ||
| 1752 | ExitOnFailure(hr, "Failed to concat separator."); | ||
| 1753 | } | ||
| 1754 | hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0); | ||
| 1755 | ExitOnFailure(hr, "Failed to concat feature."); | ||
| 1756 | break; | ||
| 1757 | } | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | if (sczAddLocal) | ||
| 1761 | { | ||
| 1762 | hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0); | ||
| 1763 | ExitOnFailure(hr, "Failed to format ADDLOCAL string."); | ||
| 1764 | |||
| 1765 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1766 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | if (sczAddSource) | ||
| 1770 | { | ||
| 1771 | hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0); | ||
| 1772 | ExitOnFailure(hr, "Failed to format ADDSOURCE string."); | ||
| 1773 | |||
| 1774 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1775 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | if (sczAddDefault) | ||
| 1779 | { | ||
| 1780 | hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0); | ||
| 1781 | ExitOnFailure(hr, "Failed to format ADDDEFAULT string."); | ||
| 1782 | |||
| 1783 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1784 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | if (sczReinstall) | ||
| 1788 | { | ||
| 1789 | hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0); | ||
| 1790 | ExitOnFailure(hr, "Failed to format REINSTALL string."); | ||
| 1791 | |||
| 1792 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1793 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | if (sczAdvertise) | ||
| 1797 | { | ||
| 1798 | hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0); | ||
| 1799 | ExitOnFailure(hr, "Failed to format ADVERTISE string."); | ||
| 1800 | |||
| 1801 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1802 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | if (sczRemove) | ||
| 1806 | { | ||
| 1807 | hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0); | ||
| 1808 | ExitOnFailure(hr, "Failed to format REMOVE string."); | ||
| 1809 | |||
| 1810 | hr = StrAllocConcatSecure(psczArguments, scz, 0); | ||
| 1811 | ExitOnFailure(hr, "Failed to concat argument string."); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | LExit: | ||
| 1815 | ReleaseStr(scz); | ||
| 1816 | ReleaseStr(sczAddLocal); | ||
| 1817 | ReleaseStr(sczAddSource); | ||
| 1818 | ReleaseStr(sczAddDefault); | ||
| 1819 | ReleaseStr(sczReinstall); | ||
| 1820 | ReleaseStr(sczAdvertise); | ||
| 1821 | ReleaseStr(sczRemove); | ||
| 1822 | |||
| 1823 | return hr; | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | static HRESULT ConcatPatchProperty( | ||
| 1827 | __in BURN_PACKAGE* pPackage, | ||
| 1828 | __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, | ||
| 1829 | __inout_z LPWSTR* psczArguments | ||
| 1830 | ) | ||
| 1831 | { | ||
| 1832 | HRESULT hr = S_OK; | ||
| 1833 | LPWSTR sczCachedDirectory = NULL; | ||
| 1834 | LPWSTR sczMspPath = NULL; | ||
| 1835 | LPWSTR sczPatches = NULL; | ||
| 1836 | |||
| 1837 | // If there are slipstream patch actions, build up their patch action. | ||
| 1838 | if (rgSlipstreamPatchActions) | ||
| 1839 | { | ||
| 1840 | for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 1841 | { | ||
| 1842 | BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; | ||
| 1843 | AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); | ||
| 1844 | |||
| 1845 | BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i]; | ||
| 1846 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) | ||
| 1847 | { | ||
| 1848 | hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); | ||
| 1849 | ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); | ||
| 1850 | |||
| 1851 | hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); | ||
| 1852 | ExitOnFailure(hr, "Failed to build MSP path."); | ||
| 1853 | |||
| 1854 | if (!sczPatches) | ||
| 1855 | { | ||
| 1856 | hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0); | ||
| 1857 | ExitOnFailure(hr, "Failed to prefix with PATCH property."); | ||
| 1858 | } | ||
| 1859 | else | ||
| 1860 | { | ||
| 1861 | hr = StrAllocConcat(&sczPatches, L";", 0); | ||
| 1862 | ExitOnFailure(hr, "Failed to semi-colon delimit patches."); | ||
| 1863 | } | ||
| 1864 | |||
| 1865 | hr = StrAllocConcat(&sczPatches, sczMspPath, 0); | ||
| 1866 | ExitOnFailure(hr, "Failed to append patch path."); | ||
| 1867 | } | ||
| 1868 | } | ||
| 1869 | |||
| 1870 | if (sczPatches) | ||
| 1871 | { | ||
| 1872 | hr = StrAllocConcat(&sczPatches, L"\"", 0); | ||
| 1873 | ExitOnFailure(hr, "Failed to close the quoted PATCH property."); | ||
| 1874 | |||
| 1875 | hr = StrAllocConcatSecure(psczArguments, sczPatches, 0); | ||
| 1876 | ExitOnFailure(hr, "Failed to append PATCH property."); | ||
| 1877 | } | ||
| 1878 | } | ||
| 1879 | |||
| 1880 | LExit: | ||
| 1881 | ReleaseStr(sczMspPath); | ||
| 1882 | ReleaseStr(sczCachedDirectory); | ||
| 1883 | ReleaseStr(sczPatches); | ||
| 1884 | return hr; | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | static void RegisterSourceDirectory( | ||
| 1888 | __in BURN_PACKAGE* pPackage, | ||
| 1889 | __in_z LPCWSTR wzMsiPath | ||
| 1890 | ) | ||
| 1891 | { | ||
| 1892 | HRESULT hr = S_OK; | ||
| 1893 | LPWSTR sczMsiDirectory = NULL; | ||
| 1894 | MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED; | ||
| 1895 | |||
| 1896 | hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory); | ||
| 1897 | ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath); | ||
| 1898 | |||
| 1899 | hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1); | ||
| 1900 | if (FAILED(hr)) | ||
| 1901 | { | ||
| 1902 | LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr); | ||
| 1903 | ExitFunction(); | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | LExit: | ||
| 1907 | ReleaseStr(sczMsiDirectory); | ||
| 1908 | |||
| 1909 | return; | ||
| 1910 | } | ||
diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h new file mode 100644 index 00000000..2a8ebfd5 --- /dev/null +++ b/src/engine/msiengine.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // function declarations | ||
| 11 | |||
| 12 | HRESULT MsiEngineParsePackageFromXml( | ||
| 13 | __in IXMLDOMNode* pixnBundle, | ||
| 14 | __in BURN_PACKAGE* pPackage | ||
| 15 | ); | ||
| 16 | HRESULT MsiEngineParsePropertiesFromXml( | ||
| 17 | __in IXMLDOMNode* pixnPackage, | ||
| 18 | __out BURN_MSIPROPERTY** prgProperties, | ||
| 19 | __out DWORD* pcProperties | ||
| 20 | ); | ||
| 21 | void MsiEnginePackageUninitialize( | ||
| 22 | __in BURN_PACKAGE* pPackage | ||
| 23 | ); | ||
| 24 | HRESULT MsiEngineDetectPackage( | ||
| 25 | __in BURN_PACKAGE* pPackage, | ||
| 26 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 27 | ); | ||
| 28 | HRESULT MsiEnginePlanCalculatePackage( | ||
| 29 | __in BURN_PACKAGE* pPackage, | ||
| 30 | __in BURN_VARIABLES* pVariables, | ||
| 31 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 32 | __out_opt BOOL* pfBARequestedCache | ||
| 33 | ); | ||
| 34 | HRESULT MsiEnginePlanAddPackage( | ||
| 35 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 36 | __in BURN_PACKAGE* pPackage, | ||
| 37 | __in BURN_PLAN* pPlan, | ||
| 38 | __in BURN_LOGGING* pLog, | ||
| 39 | __in BURN_VARIABLES* pVariables, | ||
| 40 | __in_opt HANDLE hCacheEvent, | ||
| 41 | __in BOOL fPlanPackageCacheRollback | ||
| 42 | ); | ||
| 43 | HRESULT MsiEngineAddCompatiblePackage( | ||
| 44 | __in BURN_PACKAGES* pPackages, | ||
| 45 | __in const BURN_PACKAGE* pPackage, | ||
| 46 | __out_opt BURN_PACKAGE** ppCompatiblePackage | ||
| 47 | ); | ||
| 48 | HRESULT MsiEngineExecutePackage( | ||
| 49 | __in_opt HWND hwndParent, | ||
| 50 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 51 | __in BURN_VARIABLES* pVariables, | ||
| 52 | __in BOOL fRollback, | ||
| 53 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 54 | __in LPVOID pvContext, | ||
| 55 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 56 | ); | ||
| 57 | HRESULT MsiEngineConcatProperties( | ||
| 58 | __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, | ||
| 59 | __in DWORD cProperties, | ||
| 60 | __in BURN_VARIABLES* pVariables, | ||
| 61 | __in BOOL fRollback, | ||
| 62 | __deref_out_z LPWSTR* psczProperties, | ||
| 63 | __in BOOL fObfuscateHiddenVariables | ||
| 64 | ); | ||
| 65 | INSTALLUILEVEL MsiEngineCalculateInstallUiLevel( | ||
| 66 | __in BOOL fDisplayInternalUI, | ||
| 67 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 68 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
| 69 | ); | ||
| 70 | |||
| 71 | #if defined(__cplusplus) | ||
| 72 | } | ||
| 73 | #endif | ||
diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp new file mode 100644 index 00000000..463799e6 --- /dev/null +++ b/src/engine/mspengine.cpp | |||
| @@ -0,0 +1,980 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | |||
| 9 | // structs | ||
| 10 | |||
| 11 | struct POSSIBLE_TARGETPRODUCT | ||
| 12 | { | ||
| 13 | WCHAR wzProductCode[39]; | ||
| 14 | LPWSTR pszLocalPackage; | ||
| 15 | MSIINSTALLCONTEXT context; | ||
| 16 | }; | ||
| 17 | |||
| 18 | // internal function declarations | ||
| 19 | |||
| 20 | static HRESULT GetPossibleTargetProductCodes( | ||
| 21 | __in BURN_PACKAGES* pPackages, | ||
| 22 | __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, | ||
| 23 | __inout DWORD* pcPossibleTargetProductCodes | ||
| 24 | ); | ||
| 25 | static HRESULT AddPossibleTargetProduct( | ||
| 26 | __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, | ||
| 27 | __in_z LPCWSTR wzPossibleTargetProductCode, | ||
| 28 | __in MSIINSTALLCONTEXT context, | ||
| 29 | __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, | ||
| 30 | __inout DWORD* pcPossibleTargetProducts | ||
| 31 | ); | ||
| 32 | static HRESULT AddDetectedTargetProduct( | ||
| 33 | __in BURN_PACKAGES* pPackages, | ||
| 34 | __in BURN_PACKAGE* pPackage, | ||
| 35 | __in DWORD dwOrder, | ||
| 36 | __in_z LPCWSTR wzProductCode, | ||
| 37 | __in MSIINSTALLCONTEXT context | ||
| 38 | ); | ||
| 39 | static void DeterminePatchChainedTarget( | ||
| 40 | __in BURN_PACKAGES* pPackages, | ||
| 41 | __in BURN_PACKAGE* pMspPackage, | ||
| 42 | __in LPCWSTR wzTargetProductCode, | ||
| 43 | __out BURN_PACKAGE** ppChainedTargetPackage, | ||
| 44 | __out BOOL* pfSlipstreamed | ||
| 45 | ); | ||
| 46 | static HRESULT PlanTargetProduct( | ||
| 47 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 48 | __in BOOL fRollback, | ||
| 49 | __in BURN_PLAN* pPlan, | ||
| 50 | __in BURN_LOGGING* pLog, | ||
| 51 | __in BURN_VARIABLES* pVariables, | ||
| 52 | __in BOOTSTRAPPER_ACTION_STATE actionState, | ||
| 53 | __in BURN_PACKAGE* pPackage, | ||
| 54 | __in BURN_MSPTARGETPRODUCT* pTargetProduct, | ||
| 55 | __in_opt HANDLE hCacheEvent | ||
| 56 | ); | ||
| 57 | |||
| 58 | |||
| 59 | // function definitions | ||
| 60 | |||
| 61 | extern "C" HRESULT MspEngineParsePackageFromXml( | ||
| 62 | __in IXMLDOMNode* pixnMspPackage, | ||
| 63 | __in BURN_PACKAGE* pPackage | ||
| 64 | ) | ||
| 65 | { | ||
| 66 | HRESULT hr = S_OK; | ||
| 67 | |||
| 68 | // @PatchCode | ||
| 69 | hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode); | ||
| 70 | ExitOnFailure(hr, "Failed to get @PatchCode."); | ||
| 71 | |||
| 72 | // @PatchXml | ||
| 73 | hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml); | ||
| 74 | ExitOnFailure(hr, "Failed to get @PatchXml."); | ||
| 75 | |||
| 76 | // @DisplayInternalUI | ||
| 77 | hr = XmlGetYesNoAttribute(pixnMspPackage, L"DisplayInternalUI", &pPackage->Msp.fDisplayInternalUI); | ||
| 78 | ExitOnFailure(hr, "Failed to get @DisplayInternalUI."); | ||
| 79 | |||
| 80 | // Read properties. | ||
| 81 | hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties); | ||
| 82 | ExitOnFailure(hr, "Failed to parse properties from XML."); | ||
| 83 | |||
| 84 | LExit: | ||
| 85 | |||
| 86 | return hr; | ||
| 87 | } | ||
| 88 | |||
| 89 | extern "C" void MspEnginePackageUninitialize( | ||
| 90 | __in BURN_PACKAGE* pPackage | ||
| 91 | ) | ||
| 92 | { | ||
| 93 | ReleaseStr(pPackage->Msp.sczPatchCode); | ||
| 94 | ReleaseStr(pPackage->Msp.sczApplicabilityXml); | ||
| 95 | |||
| 96 | // free properties | ||
| 97 | if (pPackage->Msp.rgProperties) | ||
| 98 | { | ||
| 99 | for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i) | ||
| 100 | { | ||
| 101 | BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i]; | ||
| 102 | |||
| 103 | ReleaseStr(pProperty->sczId); | ||
| 104 | ReleaseStr(pProperty->sczValue); | ||
| 105 | ReleaseStr(pProperty->sczRollbackValue); | ||
| 106 | } | ||
| 107 | MemFree(pPackage->Msp.rgProperties); | ||
| 108 | } | ||
| 109 | |||
| 110 | // free target products | ||
| 111 | ReleaseMem(pPackage->Msp.rgTargetProducts); | ||
| 112 | |||
| 113 | // clear struct | ||
| 114 | memset(&pPackage->Msp, 0, sizeof(pPackage->Msp)); | ||
| 115 | } | ||
| 116 | |||
| 117 | extern "C" HRESULT MspEngineDetectInitialize( | ||
| 118 | __in BURN_PACKAGES* pPackages | ||
| 119 | ) | ||
| 120 | { | ||
| 121 | AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); | ||
| 122 | |||
| 123 | HRESULT hr = S_OK; | ||
| 124 | POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; | ||
| 125 | DWORD cPossibleTargetProducts = 0; | ||
| 126 | |||
| 127 | #ifdef DEBUG | ||
| 128 | // All patch info should be initialized to zero. | ||
| 129 | for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) | ||
| 130 | { | ||
| 131 | BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; | ||
| 132 | Assert(!pPackage->Msp.cTargetProductCodes); | ||
| 133 | Assert(!pPackage->Msp.rgTargetProducts); | ||
| 134 | } | ||
| 135 | #endif | ||
| 136 | |||
| 137 | // Figure out which product codes to target on the machine. In the worst case all products on the machine | ||
| 138 | // will be returned. | ||
| 139 | hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); | ||
| 140 | ExitOnFailure(hr, "Failed to get possible target product codes."); | ||
| 141 | |||
| 142 | // Loop through possible target products, testing the collective patch applicability against each product in | ||
| 143 | // the appropriate context. Store the result with the appropriate patch package. | ||
| 144 | for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) | ||
| 145 | { | ||
| 146 | const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; | ||
| 147 | |||
| 148 | LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); | ||
| 149 | |||
| 150 | if (pPossibleTargetProduct->pszLocalPackage) | ||
| 151 | { | ||
| 152 | // Ignores current machine state to determine just patch applicability. | ||
| 153 | // Superseded and obsolesced patches will be planned separately. | ||
| 154 | hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); | ||
| 155 | } | ||
| 156 | else | ||
| 157 | { | ||
| 158 | hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); | ||
| 159 | } | ||
| 160 | |||
| 161 | if (SUCCEEDED(hr)) | ||
| 162 | { | ||
| 163 | for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) | ||
| 164 | { | ||
| 165 | if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) | ||
| 166 | { | ||
| 167 | BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; | ||
| 168 | Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); | ||
| 169 | |||
| 170 | // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. | ||
| 171 | hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); | ||
| 172 | ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); | ||
| 173 | } | ||
| 174 | // TODO: should we log something for this error case? | ||
| 175 | } | ||
| 176 | } | ||
| 177 | else | ||
| 178 | { | ||
| 179 | LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr); | ||
| 180 | } | ||
| 181 | |||
| 182 | hr = S_OK; // always reset so we test all possible target products. | ||
| 183 | } | ||
| 184 | |||
| 185 | LExit: | ||
| 186 | if (rgPossibleTargetProducts) | ||
| 187 | { | ||
| 188 | for (DWORD i = 0; i < cPossibleTargetProducts; ++i) | ||
| 189 | { | ||
| 190 | ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); | ||
| 191 | } | ||
| 192 | MemFree(rgPossibleTargetProducts); | ||
| 193 | } | ||
| 194 | |||
| 195 | return hr; | ||
| 196 | } | ||
| 197 | |||
| 198 | extern "C" HRESULT MspEngineDetectPackage( | ||
| 199 | __in BURN_PACKAGE* pPackage, | ||
| 200 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 201 | ) | ||
| 202 | { | ||
| 203 | HRESULT hr = S_OK; | ||
| 204 | LPWSTR sczState = NULL; | ||
| 205 | |||
| 206 | if (0 == pPackage->Msp.cTargetProductCodes) | ||
| 207 | { | ||
| 208 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 209 | } | ||
| 210 | else | ||
| 211 | { | ||
| 212 | // Start the package state at the the highest state then loop through all the | ||
| 213 | // target product codes and end up setting the current state to the lowest | ||
| 214 | // package state applied to the the target product codes. | ||
| 215 | pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; | ||
| 216 | |||
| 217 | for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) | ||
| 218 | { | ||
| 219 | BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; | ||
| 220 | |||
| 221 | hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); | ||
| 222 | if (SUCCEEDED(hr)) | ||
| 223 | { | ||
| 224 | switch (*sczState) | ||
| 225 | { | ||
| 226 | case '1': | ||
| 227 | pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; | ||
| 228 | break; | ||
| 229 | |||
| 230 | case '2': | ||
| 231 | pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; | ||
| 232 | break; | ||
| 233 | |||
| 234 | case '4': | ||
| 235 | pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; | ||
| 236 | break; | ||
| 237 | |||
| 238 | default: | ||
| 239 | pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 240 | break; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) | ||
| 244 | { | ||
| 245 | pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 246 | hr = S_OK; | ||
| 247 | } | ||
| 248 | ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); | ||
| 249 | |||
| 250 | if (pPackage->currentState > pTargetProduct->patchPackageState) | ||
| 251 | { | ||
| 252 | pPackage->currentState = pTargetProduct->patchPackageState; | ||
| 253 | } | ||
| 254 | |||
| 255 | hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); | ||
| 256 | ExitOnRootFailure(hr, "BA aborted detect target MSI package."); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | LExit: | ||
| 261 | ReleaseStr(sczState); | ||
| 262 | |||
| 263 | return hr; | ||
| 264 | } | ||
| 265 | |||
| 266 | // | ||
| 267 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
| 268 | // | ||
| 269 | extern "C" HRESULT MspEnginePlanCalculatePackage( | ||
| 270 | __in BURN_PACKAGE* pPackage, | ||
| 271 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 272 | __out BOOL* pfBARequestedCache | ||
| 273 | ) | ||
| 274 | { | ||
| 275 | HRESULT hr = S_OK; | ||
| 276 | BOOL fBARequestedCache = FALSE; | ||
| 277 | |||
| 278 | for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) | ||
| 279 | { | ||
| 280 | BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; | ||
| 281 | |||
| 282 | BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested; | ||
| 283 | BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 284 | BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 285 | |||
| 286 | hr = UserExperienceOnPlanTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested); | ||
| 287 | ExitOnRootFailure(hr, "BA aborted plan target MSI package."); | ||
| 288 | |||
| 289 | // Calculate the execute action. | ||
| 290 | switch (pTargetProduct->patchPackageState) | ||
| 291 | { | ||
| 292 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 293 | switch (requested) | ||
| 294 | { | ||
| 295 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 296 | execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; | ||
| 297 | break; | ||
| 298 | |||
| 299 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; | ||
| 300 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 301 | execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 302 | break; | ||
| 303 | |||
| 304 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: | ||
| 305 | execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; | ||
| 306 | break; | ||
| 307 | |||
| 308 | default: | ||
| 309 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 310 | break; | ||
| 311 | } | ||
| 312 | break; | ||
| 313 | |||
| 314 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 315 | switch (requested) | ||
| 316 | { | ||
| 317 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 318 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 319 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 320 | break; | ||
| 321 | |||
| 322 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 323 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 324 | fBARequestedCache = TRUE; | ||
| 325 | break; | ||
| 326 | |||
| 327 | default: | ||
| 328 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | break; | ||
| 332 | } | ||
| 333 | |||
| 334 | // Calculate the rollback action if there is an execute action. | ||
| 335 | if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) | ||
| 336 | { | ||
| 337 | switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) | ||
| 338 | { | ||
| 339 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 340 | switch (requested) | ||
| 341 | { | ||
| 342 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
| 343 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 344 | rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 345 | break; | ||
| 346 | |||
| 347 | default: | ||
| 348 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | break; | ||
| 352 | |||
| 353 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough; | ||
| 354 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 355 | switch (requested) | ||
| 356 | { | ||
| 357 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 358 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 359 | rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 360 | break; | ||
| 361 | |||
| 362 | default: | ||
| 363 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 364 | break; | ||
| 365 | } | ||
| 366 | break; | ||
| 367 | |||
| 368 | default: | ||
| 369 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | pTargetProduct->execute = execute; | ||
| 375 | pTargetProduct->rollback = rollback; | ||
| 376 | |||
| 377 | // The highest aggregate action state found will be returned. | ||
| 378 | if (pPackage->execute < execute) | ||
| 379 | { | ||
| 380 | pPackage->execute = execute; | ||
| 381 | } | ||
| 382 | |||
| 383 | if (pPackage->rollback < rollback) | ||
| 384 | { | ||
| 385 | pPackage->rollback = rollback; | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | if (pfBARequestedCache) | ||
| 390 | { | ||
| 391 | *pfBARequestedCache = fBARequestedCache; | ||
| 392 | } | ||
| 393 | |||
| 394 | LExit: | ||
| 395 | |||
| 396 | return hr; | ||
| 397 | } | ||
| 398 | |||
| 399 | // | ||
| 400 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
| 401 | // | ||
| 402 | extern "C" HRESULT MspEnginePlanAddPackage( | ||
| 403 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 404 | __in BURN_PACKAGE* pPackage, | ||
| 405 | __in BURN_PLAN* pPlan, | ||
| 406 | __in BURN_LOGGING* pLog, | ||
| 407 | __in BURN_VARIABLES* pVariables, | ||
| 408 | __in_opt HANDLE hCacheEvent, | ||
| 409 | __in BOOL fPlanPackageCacheRollback | ||
| 410 | ) | ||
| 411 | { | ||
| 412 | HRESULT hr = S_OK; | ||
| 413 | |||
| 414 | // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would | ||
| 415 | // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded. | ||
| 416 | // add wait for cache | ||
| 417 | if (hCacheEvent) | ||
| 418 | { | ||
| 419 | hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); | ||
| 420 | ExitOnFailure(hr, "Failed to plan package cache syncpoint"); | ||
| 421 | } | ||
| 422 | |||
| 423 | hr = DependencyPlanPackage(NULL, pPackage, pPlan); | ||
| 424 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
| 425 | |||
| 426 | // Plan the actions for each target product code. | ||
| 427 | for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) | ||
| 428 | { | ||
| 429 | BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; | ||
| 430 | |||
| 431 | // If the dependency manager changed the action state for the patch, change the target product actions. | ||
| 432 | if (pPackage->fDependencyManagerWasHere) | ||
| 433 | { | ||
| 434 | pTargetProduct->execute = pPackage->execute; | ||
| 435 | pTargetProduct->rollback = pPackage->rollback; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute) | ||
| 439 | { | ||
| 440 | hr = PlanTargetProduct(display, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent); | ||
| 441 | ExitOnFailure(hr, "Failed to plan target product."); | ||
| 442 | } | ||
| 443 | |||
| 444 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback) | ||
| 445 | { | ||
| 446 | hr = PlanTargetProduct(display, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent); | ||
| 447 | ExitOnFailure(hr, "Failed to plan rollack target product."); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | LExit: | ||
| 452 | |||
| 453 | return hr; | ||
| 454 | } | ||
| 455 | |||
| 456 | extern "C" HRESULT MspEngineExecutePackage( | ||
| 457 | __in_opt HWND hwndParent, | ||
| 458 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 459 | __in BURN_VARIABLES* pVariables, | ||
| 460 | __in BOOL fRollback, | ||
| 461 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 462 | __in LPVOID pvContext, | ||
| 463 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 464 | ) | ||
| 465 | { | ||
| 466 | HRESULT hr = S_OK; | ||
| 467 | INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); | ||
| 468 | WIU_MSI_EXECUTE_CONTEXT context = { }; | ||
| 469 | WIU_RESTART restart = WIU_RESTART_NONE; | ||
| 470 | |||
| 471 | LPWSTR sczCachedDirectory = NULL; | ||
| 472 | LPWSTR sczMspPath = NULL; | ||
| 473 | LPWSTR sczPatches = NULL; | ||
| 474 | LPWSTR sczProperties = NULL; | ||
| 475 | LPWSTR sczObfuscatedProperties = NULL; | ||
| 476 | |||
| 477 | // default to "verbose" logging | ||
| 478 | DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; | ||
| 479 | |||
| 480 | // get cached MSP paths | ||
| 481 | for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) | ||
| 482 | { | ||
| 483 | LPCWSTR wzAppend = NULL; | ||
| 484 | BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; | ||
| 485 | AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); | ||
| 486 | |||
| 487 | if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) | ||
| 488 | { | ||
| 489 | hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); | ||
| 490 | ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); | ||
| 491 | |||
| 492 | // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only | ||
| 493 | // Best effort to set the execute package cache folder variable. | ||
| 494 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); | ||
| 495 | |||
| 496 | hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); | ||
| 497 | ExitOnFailure(hr, "Failed to build MSP path."); | ||
| 498 | |||
| 499 | wzAppend = sczMspPath; | ||
| 500 | } | ||
| 501 | else // uninstall | ||
| 502 | { | ||
| 503 | wzAppend = pMspPackage->Msp.sczPatchCode; | ||
| 504 | } | ||
| 505 | |||
| 506 | if (NULL != sczPatches) | ||
| 507 | { | ||
| 508 | hr = StrAllocConcat(&sczPatches, L";", 0); | ||
| 509 | ExitOnFailure(hr, "Failed to semi-colon delimit patches."); | ||
| 510 | } | ||
| 511 | |||
| 512 | hr = StrAllocConcat(&sczPatches, wzAppend, 0); | ||
| 513 | ExitOnFailure(hr, "Failed to append patch."); | ||
| 514 | } | ||
| 515 | |||
| 516 | // Best effort to set the execute package action variable. | ||
| 517 | VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); | ||
| 518 | |||
| 519 | // Wire up the external UI handler and logging. | ||
| 520 | hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); | ||
| 521 | ExitOnFailure(hr, "Failed to initialize external UI handler."); | ||
| 522 | |||
| 523 | //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) | ||
| 524 | //{ | ||
| 525 | // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; | ||
| 526 | //} | ||
| 527 | |||
| 528 | if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) | ||
| 529 | { | ||
| 530 | hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); | ||
| 531 | ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); | ||
| 532 | } | ||
| 533 | |||
| 534 | // set up properties | ||
| 535 | hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); | ||
| 536 | ExitOnFailure(hr, "Failed to add properties to argument string."); | ||
| 537 | |||
| 538 | hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); | ||
| 539 | ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); | ||
| 540 | |||
| 541 | LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); | ||
| 542 | |||
| 543 | // | ||
| 544 | // Do the actual action. | ||
| 545 | // | ||
| 546 | switch (pExecuteAction->mspTarget.action) | ||
| 547 | { | ||
| 548 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; | ||
| 549 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | ||
| 550 | hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); | ||
| 551 | ExitOnFailure(hr, "Failed to add PATCH property on install."); | ||
| 552 | |||
| 553 | hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); | ||
| 554 | ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); | ||
| 555 | |||
| 556 | hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); | ||
| 557 | ExitOnFailure(hr, "Failed to add reboot suppression property on install."); | ||
| 558 | |||
| 559 | hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); | ||
| 560 | ExitOnFailure(hr, "Failed to install MSP package."); | ||
| 561 | break; | ||
| 562 | |||
| 563 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 564 | hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); | ||
| 565 | ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); | ||
| 566 | |||
| 567 | // Ignore all dependencies, since the Burn engine already performed the check. | ||
| 568 | hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); | ||
| 569 | ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); | ||
| 570 | |||
| 571 | hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); | ||
| 572 | ExitOnFailure(hr, "Failed to uninstall MSP package."); | ||
| 573 | break; | ||
| 574 | } | ||
| 575 | |||
| 576 | LExit: | ||
| 577 | WiuUninitializeExternalUI(&context); | ||
| 578 | |||
| 579 | ReleaseStr(sczCachedDirectory); | ||
| 580 | ReleaseStr(sczMspPath); | ||
| 581 | StrSecureZeroFreeString(sczProperties); | ||
| 582 | ReleaseStr(sczObfuscatedProperties); | ||
| 583 | ReleaseStr(sczPatches); | ||
| 584 | |||
| 585 | switch (restart) | ||
| 586 | { | ||
| 587 | case WIU_RESTART_NONE: | ||
| 588 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 589 | break; | ||
| 590 | |||
| 591 | case WIU_RESTART_REQUIRED: | ||
| 592 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 593 | break; | ||
| 594 | |||
| 595 | case WIU_RESTART_INITIATED: | ||
| 596 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; | ||
| 597 | break; | ||
| 598 | } | ||
| 599 | |||
| 600 | // Best effort to clear the execute package cache folder and action variables. | ||
| 601 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); | ||
| 602 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); | ||
| 603 | |||
| 604 | return hr; | ||
| 605 | } | ||
| 606 | |||
| 607 | extern "C" void MspEngineSlipstreamUpdateState( | ||
| 608 | __in BURN_PACKAGE* pPackage, | ||
| 609 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 610 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 611 | ) | ||
| 612 | { | ||
| 613 | Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type); | ||
| 614 | |||
| 615 | // If the dependency manager set our state then that means something else | ||
| 616 | // is dependent on our package. That trumps whatever the slipstream update | ||
| 617 | // state might set. | ||
| 618 | if (!pPackage->fDependencyManagerWasHere) | ||
| 619 | { | ||
| 620 | // The highest aggregate action state found will be returned. | ||
| 621 | if (pPackage->execute < execute) | ||
| 622 | { | ||
| 623 | pPackage->execute = execute; | ||
| 624 | } | ||
| 625 | |||
| 626 | if (pPackage->rollback < rollback) | ||
| 627 | { | ||
| 628 | pPackage->rollback = rollback; | ||
| 629 | } | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | |||
| 634 | // internal helper functions | ||
| 635 | |||
| 636 | static HRESULT GetPossibleTargetProductCodes( | ||
| 637 | __in BURN_PACKAGES* pPackages, | ||
| 638 | __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, | ||
| 639 | __inout DWORD* pcPossibleTargetProducts | ||
| 640 | ) | ||
| 641 | { | ||
| 642 | HRESULT hr = S_OK; | ||
| 643 | STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL; | ||
| 644 | BOOL fCheckAll = FALSE; | ||
| 645 | WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1]; | ||
| 646 | |||
| 647 | // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up | ||
| 648 | // doing patch applicability for the same product code multiple times and that would confuse | ||
| 649 | // everything down stream. | ||
| 650 | hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE); | ||
| 651 | ExitOnFailure(hr, "Failed to create unique target product codes."); | ||
| 652 | |||
| 653 | // If the patches target a specific set of product/upgrade codes, search only those. This | ||
| 654 | // should be much faster than searching all packages on the machine. | ||
| 655 | if (pPackages->rgPatchTargetCodes) | ||
| 656 | { | ||
| 657 | for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) | ||
| 658 | { | ||
| 659 | BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; | ||
| 660 | |||
| 661 | // If targeting a product, add the unique product code to the list. | ||
| 662 | if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type) | ||
| 663 | { | ||
| 664 | hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); | ||
| 665 | ExitOnFailure(hr, "Failed to add product code to possible target product codes."); | ||
| 666 | } | ||
| 667 | else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type) | ||
| 668 | { | ||
| 669 | // Enumerate all unique related products to the target upgrade code. | ||
| 670 | for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) | ||
| 671 | { | ||
| 672 | hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode); | ||
| 673 | if (SUCCEEDED(hr)) | ||
| 674 | { | ||
| 675 | hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts); | ||
| 676 | ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes."); | ||
| 677 | } | ||
| 678 | else if (E_BADCONFIGURATION == hr) | ||
| 679 | { | ||
| 680 | // Skip product's with bad configuration and continue. | ||
| 681 | LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); | ||
| 682 | |||
| 683 | hr = S_OK; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | if (E_NOMOREITEMS == hr) | ||
| 688 | { | ||
| 689 | hr = S_OK; | ||
| 690 | } | ||
| 691 | ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode); | ||
| 692 | } | ||
| 693 | else | ||
| 694 | { | ||
| 695 | // The element does not target a specific product. | ||
| 696 | fCheckAll = TRUE; | ||
| 697 | |||
| 698 | break; | ||
| 699 | } | ||
| 700 | } | ||
| 701 | } | ||
| 702 | else | ||
| 703 | { | ||
| 704 | fCheckAll = TRUE; | ||
| 705 | } | ||
| 706 | |||
| 707 | // One or more of the patches do not target a specific product so search everything on the machine. | ||
| 708 | if (fCheckAll) | ||
| 709 | { | ||
| 710 | for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct) | ||
| 711 | { | ||
| 712 | MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE; | ||
| 713 | |||
| 714 | hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL); | ||
| 715 | if (SUCCEEDED(hr)) | ||
| 716 | { | ||
| 717 | hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts); | ||
| 718 | ExitOnFailure(hr, "Failed to add product code to search product codes."); | ||
| 719 | } | ||
| 720 | else if (E_BADCONFIGURATION == hr) | ||
| 721 | { | ||
| 722 | // Skip products with bad configuration and continue. | ||
| 723 | LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode); | ||
| 724 | |||
| 725 | hr = S_OK; | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | if (E_NOMOREITEMS == hr) | ||
| 730 | { | ||
| 731 | hr = S_OK; | ||
| 732 | } | ||
| 733 | ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability."); | ||
| 734 | } | ||
| 735 | |||
| 736 | LExit: | ||
| 737 | ReleaseDict(sdUniquePossibleTargetProductCodes); | ||
| 738 | |||
| 739 | return hr; | ||
| 740 | } | ||
| 741 | |||
| 742 | static HRESULT AddPossibleTargetProduct( | ||
| 743 | __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes, | ||
| 744 | __in_z LPCWSTR wzPossibleTargetProductCode, | ||
| 745 | __in MSIINSTALLCONTEXT context, | ||
| 746 | __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts, | ||
| 747 | __inout DWORD* pcPossibleTargetProducts | ||
| 748 | ) | ||
| 749 | { | ||
| 750 | HRESULT hr = S_OK; | ||
| 751 | LPWSTR pszLocalPackage = NULL; | ||
| 752 | |||
| 753 | // Only add this possible target code if we haven't queried for it already. | ||
| 754 | if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode)) | ||
| 755 | { | ||
| 756 | // If the install context is not known, ask the Windows Installer for it. If we can't get the context | ||
| 757 | // then bail. | ||
| 758 | if (MSIINSTALLCONTEXT_NONE == context) | ||
| 759 | { | ||
| 760 | hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL); | ||
| 761 | if (FAILED(hr)) | ||
| 762 | { | ||
| 763 | ExitFunction1(hr = S_OK); | ||
| 764 | } | ||
| 765 | } | ||
| 766 | |||
| 767 | hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode); | ||
| 768 | ExitOnFailure(hr, "Failed to add possible target code to unique product codes."); | ||
| 769 | |||
| 770 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3); | ||
| 771 | ExitOnFailure(hr, "Failed to grow array of possible target products."); | ||
| 772 | |||
| 773 | POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts; | ||
| 774 | |||
| 775 | hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode); | ||
| 776 | ExitOnFailure(hr, "Failed to copy possible target product code."); | ||
| 777 | |||
| 778 | // Attempt to get the local package path so we can more quickly determine patch applicability later. | ||
| 779 | hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage); | ||
| 780 | if (SUCCEEDED(hr)) | ||
| 781 | { | ||
| 782 | pPossibleTargetProduct->pszLocalPackage = pszLocalPackage; | ||
| 783 | pszLocalPackage = NULL; | ||
| 784 | } | ||
| 785 | else | ||
| 786 | { | ||
| 787 | // Will instead call MsiDeterminePatchSequence later. | ||
| 788 | hr = S_OK; | ||
| 789 | } | ||
| 790 | |||
| 791 | pPossibleTargetProduct->context = context; | ||
| 792 | |||
| 793 | ++(*pcPossibleTargetProducts); | ||
| 794 | } | ||
| 795 | |||
| 796 | LExit: | ||
| 797 | ReleaseStr(pszLocalPackage); | ||
| 798 | |||
| 799 | return hr; | ||
| 800 | } | ||
| 801 | |||
| 802 | static HRESULT AddDetectedTargetProduct( | ||
| 803 | __in BURN_PACKAGES* pPackages, | ||
| 804 | __in BURN_PACKAGE* pPackage, | ||
| 805 | __in DWORD dwOrder, | ||
| 806 | __in_z LPCWSTR wzProductCode, | ||
| 807 | __in MSIINSTALLCONTEXT context | ||
| 808 | ) | ||
| 809 | { | ||
| 810 | HRESULT hr = S_OK; | ||
| 811 | |||
| 812 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5); | ||
| 813 | ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated."); | ||
| 814 | |||
| 815 | hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode); | ||
| 816 | ExitOnFailure(hr, "Failed to copy target product code."); | ||
| 817 | |||
| 818 | DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode, | ||
| 819 | &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage, | ||
| 820 | &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream); | ||
| 821 | |||
| 822 | pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context; | ||
| 823 | pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder; | ||
| 824 | ++pPackage->Msp.cTargetProductCodes; | ||
| 825 | |||
| 826 | LExit: | ||
| 827 | return hr; | ||
| 828 | } | ||
| 829 | |||
| 830 | static void DeterminePatchChainedTarget( | ||
| 831 | __in BURN_PACKAGES* pPackages, | ||
| 832 | __in BURN_PACKAGE* pMspPackage, | ||
| 833 | __in LPCWSTR wzTargetProductCode, | ||
| 834 | __out BURN_PACKAGE** ppChainedTargetPackage, | ||
| 835 | __out BOOL* pfSlipstreamed | ||
| 836 | ) | ||
| 837 | { | ||
| 838 | BURN_PACKAGE* pTargetMsiPackage = NULL; | ||
| 839 | BOOL fSlipstreamed = FALSE; | ||
| 840 | |||
| 841 | for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) | ||
| 842 | { | ||
| 843 | BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; | ||
| 844 | |||
| 845 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1)) | ||
| 846 | { | ||
| 847 | pTargetMsiPackage = pPackage; | ||
| 848 | |||
| 849 | for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) | ||
| 850 | { | ||
| 851 | BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j]; | ||
| 852 | if (pSlipstreamMsp == pMspPackage) | ||
| 853 | { | ||
| 854 | AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once."); | ||
| 855 | fSlipstreamed = TRUE; | ||
| 856 | break; | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | break; | ||
| 861 | } | ||
| 862 | } | ||
| 863 | |||
| 864 | *ppChainedTargetPackage = pTargetMsiPackage; | ||
| 865 | *pfSlipstreamed = fSlipstreamed; | ||
| 866 | |||
| 867 | return; | ||
| 868 | } | ||
| 869 | |||
| 870 | static HRESULT PlanTargetProduct( | ||
| 871 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 872 | __in BOOL fRollback, | ||
| 873 | __in BURN_PLAN* pPlan, | ||
| 874 | __in BURN_LOGGING* pLog, | ||
| 875 | __in BURN_VARIABLES* pVariables, | ||
| 876 | __in BOOTSTRAPPER_ACTION_STATE actionState, | ||
| 877 | __in BURN_PACKAGE* pPackage, | ||
| 878 | __in BURN_MSPTARGETPRODUCT* pTargetProduct, | ||
| 879 | __in_opt HANDLE hCacheEvent | ||
| 880 | ) | ||
| 881 | { | ||
| 882 | HRESULT hr = S_OK; | ||
| 883 | BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions; | ||
| 884 | DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions; | ||
| 885 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 886 | DWORD dwInsertSequence = 0; | ||
| 887 | |||
| 888 | // Try to find another MSP action with the exact same action (install or uninstall) targeting | ||
| 889 | // the same product in the same machine context (per-user or per-machine). | ||
| 890 | for (DWORD i = 0; i < cActions; ++i) | ||
| 891 | { | ||
| 892 | pAction = rgActions + i; | ||
| 893 | |||
| 894 | if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && | ||
| 895 | pAction->mspTarget.action == actionState && | ||
| 896 | pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) && | ||
| 897 | CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1)) | ||
| 898 | { | ||
| 899 | dwInsertSequence = i; | ||
| 900 | break; | ||
| 901 | } | ||
| 902 | |||
| 903 | pAction = NULL; | ||
| 904 | } | ||
| 905 | |||
| 906 | // If we didn't find an MSP target action already updating the product, create a new action. | ||
| 907 | if (!pAction) | ||
| 908 | { | ||
| 909 | if (fRollback) | ||
| 910 | { | ||
| 911 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 912 | } | ||
| 913 | else | ||
| 914 | { | ||
| 915 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 916 | } | ||
| 917 | ExitOnFailure(hr, "Failed to plan action for target product."); | ||
| 918 | |||
| 919 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET; | ||
| 920 | pAction->mspTarget.action = actionState; | ||
| 921 | pAction->mspTarget.pPackage = pPackage; | ||
| 922 | pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context); | ||
| 923 | pAction->mspTarget.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msp.fDisplayInternalUI, display, pAction->mspTarget.action); | ||
| 924 | pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage; | ||
| 925 | pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream; | ||
| 926 | hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0); | ||
| 927 | ExitOnFailure(hr, "Failed to copy target product code."); | ||
| 928 | |||
| 929 | // If this is a per-machine target product, then the plan needs to be per-machine as well. | ||
| 930 | if (pAction->mspTarget.fPerMachineTarget) | ||
| 931 | { | ||
| 932 | pPlan->fPerMachine = TRUE; | ||
| 933 | } | ||
| 934 | |||
| 935 | LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors. | ||
| 936 | } | ||
| 937 | else | ||
| 938 | { | ||
| 939 | if (!fRollback && hCacheEvent) | ||
| 940 | { | ||
| 941 | // Since a previouse MSP target action is being updated with the new MSP, | ||
| 942 | // insert a wait syncpoint to before this action since we need to cache the current MSI before using it. | ||
| 943 | BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL; | ||
| 944 | hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction); | ||
| 945 | ExitOnFailure(hr, "Failed to insert execute action."); | ||
| 946 | |||
| 947 | pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; | ||
| 948 | pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent; | ||
| 949 | |||
| 950 | // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer. | ||
| 951 | pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1); | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | // Add our target product to the array and sort based on their order determined during detection. | ||
| 956 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2); | ||
| 957 | ExitOnFailure(hr, "Failed grow array of ordered patches."); | ||
| 958 | |||
| 959 | pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].dwOrder = pTargetProduct->dwOrder; | ||
| 960 | pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage; | ||
| 961 | ++pAction->mspTarget.cOrderedPatches; | ||
| 962 | |||
| 963 | // Insertion sort to keep the patches ordered. | ||
| 964 | for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i) | ||
| 965 | { | ||
| 966 | if (pAction->mspTarget.rgOrderedPatches[i].dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].dwOrder) | ||
| 967 | { | ||
| 968 | BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1]; | ||
| 969 | pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i]; | ||
| 970 | pAction->mspTarget.rgOrderedPatches[i] = temp; | ||
| 971 | } | ||
| 972 | else // no swap necessary, we're done. | ||
| 973 | { | ||
| 974 | break; | ||
| 975 | } | ||
| 976 | } | ||
| 977 | |||
| 978 | LExit: | ||
| 979 | return hr; | ||
| 980 | } | ||
diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h new file mode 100644 index 00000000..9f585720 --- /dev/null +++ b/src/engine/mspengine.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | |||
| 13 | // structures | ||
| 14 | |||
| 15 | |||
| 16 | // typedefs | ||
| 17 | |||
| 18 | |||
| 19 | // function declarations | ||
| 20 | |||
| 21 | HRESULT MspEngineParsePackageFromXml( | ||
| 22 | __in IXMLDOMNode* pixnBundle, | ||
| 23 | __in BURN_PACKAGE* pPackage | ||
| 24 | ); | ||
| 25 | void MspEnginePackageUninitialize( | ||
| 26 | __in BURN_PACKAGE* pPackage | ||
| 27 | ); | ||
| 28 | HRESULT MspEngineDetectInitialize( | ||
| 29 | __in BURN_PACKAGES* pPackages | ||
| 30 | ); | ||
| 31 | HRESULT MspEngineDetectPackage( | ||
| 32 | __in BURN_PACKAGE* pPackage, | ||
| 33 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 34 | ); | ||
| 35 | HRESULT MspEnginePlanCalculatePackage( | ||
| 36 | __in BURN_PACKAGE* pPackage, | ||
| 37 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 38 | __out_opt BOOL* pfBARequestedCache | ||
| 39 | ); | ||
| 40 | HRESULT MspEnginePlanAddPackage( | ||
| 41 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 42 | __in BURN_PACKAGE* pPackage, | ||
| 43 | __in BURN_PLAN* pPlan, | ||
| 44 | __in BURN_LOGGING* pLog, | ||
| 45 | __in BURN_VARIABLES* pVariables, | ||
| 46 | __in_opt HANDLE hCacheEvent, | ||
| 47 | __in BOOL fPlanPackageCacheRollback | ||
| 48 | ); | ||
| 49 | HRESULT MspEngineExecutePackage( | ||
| 50 | __in_opt HWND hwndParent, | ||
| 51 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 52 | __in BURN_VARIABLES* pVariables, | ||
| 53 | __in BOOL fRollback, | ||
| 54 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
| 55 | __in LPVOID pvContext, | ||
| 56 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 57 | ); | ||
| 58 | void MspEngineSlipstreamUpdateState( | ||
| 59 | __in BURN_PACKAGE* pMspPackage, | ||
| 60 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 61 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 62 | ); | ||
| 63 | |||
| 64 | |||
| 65 | #if defined(__cplusplus) | ||
| 66 | } | ||
| 67 | #endif | ||
diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp new file mode 100644 index 00000000..3818e932 --- /dev/null +++ b/src/engine/msuengine.cpp | |||
| @@ -0,0 +1,513 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | #define WU_S_REBOOT_REQUIRED 0x00240005L | ||
| 9 | #define WU_S_ALREADY_INSTALLED 0x00240006L | ||
| 10 | |||
| 11 | |||
| 12 | // function definitions | ||
| 13 | static HRESULT EnsureWUServiceEnabled( | ||
| 14 | __in BOOL fStopWusaService, | ||
| 15 | __out SC_HANDLE* pschWu, | ||
| 16 | __out BOOL* pfPreviouslyDisabled | ||
| 17 | ); | ||
| 18 | static HRESULT SetServiceStartType( | ||
| 19 | __in SC_HANDLE sch, | ||
| 20 | __in DWORD stratType | ||
| 21 | ); | ||
| 22 | static HRESULT StopWUService( | ||
| 23 | __in SC_HANDLE schWu | ||
| 24 | ); | ||
| 25 | |||
| 26 | |||
| 27 | extern "C" HRESULT MsuEngineParsePackageFromXml( | ||
| 28 | __in IXMLDOMNode* pixnMsuPackage, | ||
| 29 | __in BURN_PACKAGE* pPackage | ||
| 30 | ) | ||
| 31 | { | ||
| 32 | HRESULT hr = S_OK; | ||
| 33 | |||
| 34 | // @KB | ||
| 35 | hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB); | ||
| 36 | ExitOnFailure(hr, "Failed to get @KB."); | ||
| 37 | |||
| 38 | // @DetectCondition | ||
| 39 | hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition); | ||
| 40 | ExitOnFailure(hr, "Failed to get @DetectCondition."); | ||
| 41 | |||
| 42 | LExit: | ||
| 43 | return hr; | ||
| 44 | } | ||
| 45 | |||
| 46 | extern "C" void MsuEnginePackageUninitialize( | ||
| 47 | __in BURN_PACKAGE* pPackage | ||
| 48 | ) | ||
| 49 | { | ||
| 50 | ReleaseNullStr(pPackage->Msu.sczKB); | ||
| 51 | ReleaseNullStr(pPackage->Msu.sczDetectCondition); | ||
| 52 | } | ||
| 53 | |||
| 54 | extern "C" HRESULT MsuEngineDetectPackage( | ||
| 55 | __in BURN_PACKAGE* pPackage, | ||
| 56 | __in BURN_VARIABLES* pVariables | ||
| 57 | ) | ||
| 58 | { | ||
| 59 | HRESULT hr = S_OK; | ||
| 60 | BOOL fDetected = FALSE; | ||
| 61 | |||
| 62 | // evaluate detect condition | ||
| 63 | if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition) | ||
| 64 | { | ||
| 65 | hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected); | ||
| 66 | ExitOnFailure(hr, "Failed to evaluate MSU package detect condition."); | ||
| 67 | } | ||
| 68 | |||
| 69 | // update detect state | ||
| 70 | pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
| 71 | |||
| 72 | LExit: | ||
| 73 | return hr; | ||
| 74 | } | ||
| 75 | |||
| 76 | // | ||
| 77 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
| 78 | // | ||
| 79 | extern "C" HRESULT MsuEnginePlanCalculatePackage( | ||
| 80 | __in BURN_PACKAGE* pPackage, | ||
| 81 | __out_opt BOOL* pfBARequestedCache | ||
| 82 | ) | ||
| 83 | { | ||
| 84 | HRESULT hr = S_OK; | ||
| 85 | BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 86 | BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 87 | BOOL fBARequestedCache = FALSE; | ||
| 88 | |||
| 89 | BOOL fAllowUninstall = FALSE; | ||
| 90 | OS_VERSION osVersion = OS_VERSION_UNKNOWN; | ||
| 91 | DWORD dwServicePack = 0; | ||
| 92 | |||
| 93 | // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. | ||
| 94 | OsGetVersion(&osVersion, &dwServicePack); | ||
| 95 | fAllowUninstall = (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion; | ||
| 96 | |||
| 97 | // execute action | ||
| 98 | switch (pPackage->currentState) | ||
| 99 | { | ||
| 100 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 101 | switch (pPackage->requested) | ||
| 102 | { | ||
| 103 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 104 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 105 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 106 | break; | ||
| 107 | |||
| 108 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; | ||
| 109 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 110 | execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 111 | break; | ||
| 112 | |||
| 113 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: | ||
| 114 | execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 115 | break; | ||
| 116 | |||
| 117 | default: | ||
| 118 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | break; | ||
| 122 | |||
| 123 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 124 | switch (pPackage->requested) | ||
| 125 | { | ||
| 126 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 127 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 128 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 129 | break; | ||
| 130 | |||
| 131 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
| 132 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 133 | fBARequestedCache = TRUE; | ||
| 134 | |||
| 135 | default: | ||
| 136 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | break; | ||
| 140 | |||
| 141 | default: | ||
| 142 | hr = E_INVALIDARG; | ||
| 143 | ExitOnRootFailure(hr, "Invalid package state."); | ||
| 144 | } | ||
| 145 | |||
| 146 | // Calculate the rollback action if there is an execute action. | ||
| 147 | if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) | ||
| 148 | { | ||
| 149 | switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) | ||
| 150 | { | ||
| 151 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 152 | switch (pPackage->requested) | ||
| 153 | { | ||
| 154 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
| 155 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
| 156 | rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
| 157 | break; | ||
| 158 | |||
| 159 | default: | ||
| 160 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | break; | ||
| 164 | |||
| 165 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 166 | switch (pPackage->requested) | ||
| 167 | { | ||
| 168 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
| 169 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
| 170 | rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 171 | break; | ||
| 172 | |||
| 173 | default: | ||
| 174 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | break; | ||
| 178 | |||
| 179 | default: | ||
| 180 | hr = E_INVALIDARG; | ||
| 181 | ExitOnRootFailure(hr, "Invalid package expected state."); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | // return values | ||
| 186 | pPackage->execute = execute; | ||
| 187 | pPackage->rollback = rollback; | ||
| 188 | |||
| 189 | if (pfBARequestedCache) | ||
| 190 | { | ||
| 191 | *pfBARequestedCache = fBARequestedCache; | ||
| 192 | } | ||
| 193 | |||
| 194 | LExit: | ||
| 195 | return hr; | ||
| 196 | } | ||
| 197 | |||
| 198 | // | ||
| 199 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
| 200 | // | ||
| 201 | extern "C" HRESULT MsuEnginePlanAddPackage( | ||
| 202 | __in BURN_PACKAGE* pPackage, | ||
| 203 | __in BURN_PLAN* pPlan, | ||
| 204 | __in BURN_LOGGING* pLog, | ||
| 205 | __in BURN_VARIABLES* pVariables, | ||
| 206 | __in HANDLE hCacheEvent, | ||
| 207 | __in BOOL fPlanPackageCacheRollback | ||
| 208 | ) | ||
| 209 | { | ||
| 210 | HRESULT hr = S_OK; | ||
| 211 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 212 | |||
| 213 | // add wait for cache | ||
| 214 | if (hCacheEvent) | ||
| 215 | { | ||
| 216 | hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback); | ||
| 217 | ExitOnFailure(hr, "Failed to plan package cache syncpoint"); | ||
| 218 | } | ||
| 219 | |||
| 220 | hr = DependencyPlanPackage(NULL, pPackage, pPlan); | ||
| 221 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
| 222 | |||
| 223 | // add execute action | ||
| 224 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) | ||
| 225 | { | ||
| 226 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 227 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 228 | |||
| 229 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; | ||
| 230 | pAction->msuPackage.pPackage = pPackage; | ||
| 231 | pAction->msuPackage.action = pPackage->execute; | ||
| 232 | |||
| 233 | LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. | ||
| 234 | } | ||
| 235 | |||
| 236 | // add rollback action | ||
| 237 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) | ||
| 238 | { | ||
| 239 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 240 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 241 | |||
| 242 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; | ||
| 243 | pAction->msuPackage.pPackage = pPackage; | ||
| 244 | pAction->msuPackage.action = pPackage->rollback; | ||
| 245 | |||
| 246 | LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. | ||
| 247 | } | ||
| 248 | |||
| 249 | LExit: | ||
| 250 | return hr; | ||
| 251 | } | ||
| 252 | |||
| 253 | extern "C" HRESULT MsuEngineExecutePackage( | ||
| 254 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 255 | __in BURN_VARIABLES* pVariables, | ||
| 256 | __in BOOL fRollback, | ||
| 257 | __in BOOL fStopWusaService, | ||
| 258 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 259 | __in LPVOID pvContext, | ||
| 260 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 261 | ) | ||
| 262 | { | ||
| 263 | HRESULT hr = S_OK; | ||
| 264 | int nResult = IDNOACTION; | ||
| 265 | LPWSTR sczCachedDirectory = NULL; | ||
| 266 | LPWSTR sczMsuPath = NULL; | ||
| 267 | LPWSTR sczWindowsPath = NULL; | ||
| 268 | LPWSTR sczSystemPath = NULL; | ||
| 269 | LPWSTR sczWusaPath = NULL; | ||
| 270 | LPWSTR sczCommand = NULL; | ||
| 271 | SC_HANDLE schWu = NULL; | ||
| 272 | BOOL fWuWasDisabled = FALSE; | ||
| 273 | STARTUPINFOW si = { }; | ||
| 274 | PROCESS_INFORMATION pi = { }; | ||
| 275 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 276 | DWORD dwExitCode = 0; | ||
| 277 | BOOL fUseSysNativePath = FALSE; | ||
| 278 | |||
| 279 | #if !defined(_WIN64) | ||
| 280 | hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); | ||
| 281 | ExitOnFailure(hr, "Failed to determine WOW64 status."); | ||
| 282 | #endif | ||
| 283 | |||
| 284 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
| 285 | |||
| 286 | // get wusa.exe path | ||
| 287 | if (fUseSysNativePath) | ||
| 288 | { | ||
| 289 | hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); | ||
| 290 | ExitOnFailure(hr, "Failed to find Windows directory."); | ||
| 291 | |||
| 292 | hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath); | ||
| 293 | ExitOnFailure(hr, "Failed to append SysNative directory."); | ||
| 294 | } | ||
| 295 | else | ||
| 296 | { | ||
| 297 | hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); | ||
| 298 | ExitOnFailure(hr, "Failed to find System32 directory."); | ||
| 299 | } | ||
| 300 | |||
| 301 | hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); | ||
| 302 | ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); | ||
| 303 | |||
| 304 | // build command | ||
| 305 | switch (pExecuteAction->msuPackage.action) | ||
| 306 | { | ||
| 307 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
| 308 | // get cached MSU path | ||
| 309 | hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); | ||
| 310 | ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); | ||
| 311 | |||
| 312 | // Best effort to set the execute package cache folder variable. | ||
| 313 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); | ||
| 314 | |||
| 315 | hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); | ||
| 316 | ExitOnFailure(hr, "Failed to build MSU path."); | ||
| 317 | |||
| 318 | // format command | ||
| 319 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); | ||
| 320 | ExitOnFailure(hr, "Failed to format MSU install command."); | ||
| 321 | break; | ||
| 322 | |||
| 323 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
| 324 | // format command | ||
| 325 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); | ||
| 326 | ExitOnFailure(hr, "Failed to format MSU uninstall command."); | ||
| 327 | break; | ||
| 328 | |||
| 329 | default: | ||
| 330 | hr = E_UNEXPECTED; | ||
| 331 | ExitOnFailure(hr, "Failed to get action arguments for MSU package."); | ||
| 332 | } | ||
| 333 | |||
| 334 | if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) | ||
| 335 | { | ||
| 336 | hr = StrAllocConcat(&sczCommand, L" /log:", 0); | ||
| 337 | ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); | ||
| 338 | |||
| 339 | hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); | ||
| 340 | ExitOnFailure(hr, "Failed to append log path to MSU command-line."); | ||
| 341 | } | ||
| 342 | |||
| 343 | LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); | ||
| 344 | |||
| 345 | hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); | ||
| 346 | ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); | ||
| 347 | |||
| 348 | // create process | ||
| 349 | si.cb = sizeof(si); | ||
| 350 | if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) | ||
| 351 | { | ||
| 352 | ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); | ||
| 353 | } | ||
| 354 | |||
| 355 | do | ||
| 356 | { | ||
| 357 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 358 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 359 | message.progress.dwPercentage = 50; | ||
| 360 | nResult = pfnGenericMessageHandler(&message, pvContext); | ||
| 361 | hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
| 362 | ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); | ||
| 363 | |||
| 364 | // wait for process to terminate | ||
| 365 | hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); | ||
| 366 | if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) | ||
| 367 | { | ||
| 368 | ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); | ||
| 369 | } | ||
| 370 | } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); | ||
| 371 | |||
| 372 | // get process exit code | ||
| 373 | if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) | ||
| 374 | { | ||
| 375 | ExitWithLastError(hr, "Failed to get process exit code."); | ||
| 376 | } | ||
| 377 | |||
| 378 | // We'll normalize the restart required error code from wusa.exe just in case. Most likely | ||
| 379 | // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. | ||
| 380 | if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) | ||
| 381 | { | ||
| 382 | dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; | ||
| 383 | } | ||
| 384 | |||
| 385 | // handle exit code | ||
| 386 | switch (dwExitCode) | ||
| 387 | { | ||
| 388 | case S_OK: __fallthrough; | ||
| 389 | case S_FALSE: __fallthrough; | ||
| 390 | case WU_S_ALREADY_INSTALLED: | ||
| 391 | hr = S_OK; | ||
| 392 | break; | ||
| 393 | |||
| 394 | case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; | ||
| 395 | case WU_S_REBOOT_REQUIRED: | ||
| 396 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
| 397 | hr = S_OK; | ||
| 398 | break; | ||
| 399 | |||
| 400 | default: | ||
| 401 | hr = static_cast<HRESULT>(dwExitCode); | ||
| 402 | break; | ||
| 403 | } | ||
| 404 | |||
| 405 | LExit: | ||
| 406 | ReleaseStr(sczCachedDirectory); | ||
| 407 | ReleaseStr(sczMsuPath); | ||
| 408 | ReleaseStr(sczSystemPath); | ||
| 409 | ReleaseStr(sczWindowsPath); | ||
| 410 | ReleaseStr(sczWusaPath); | ||
| 411 | ReleaseStr(sczCommand); | ||
| 412 | |||
| 413 | ReleaseHandle(pi.hProcess); | ||
| 414 | ReleaseHandle(pi.hThread); | ||
| 415 | |||
| 416 | if (fWuWasDisabled) | ||
| 417 | { | ||
| 418 | SetServiceStartType(schWu, SERVICE_DISABLED); | ||
| 419 | } | ||
| 420 | |||
| 421 | // Best effort to clear the execute package cache folder variable. | ||
| 422 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); | ||
| 423 | |||
| 424 | return hr; | ||
| 425 | } | ||
| 426 | |||
| 427 | static HRESULT EnsureWUServiceEnabled( | ||
| 428 | __in BOOL fStopWusaService, | ||
| 429 | __out SC_HANDLE* pschWu, | ||
| 430 | __out BOOL* pfPreviouslyDisabled | ||
| 431 | ) | ||
| 432 | { | ||
| 433 | HRESULT hr = S_OK; | ||
| 434 | SC_HANDLE schSCM = NULL; | ||
| 435 | SC_HANDLE schWu = NULL; | ||
| 436 | SERVICE_STATUS serviceStatus = { }; | ||
| 437 | QUERY_SERVICE_CONFIGW* pConfig = NULL; | ||
| 438 | |||
| 439 | schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); | ||
| 440 | ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager."); | ||
| 441 | |||
| 442 | schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP ); | ||
| 443 | ExitOnNullWithLastError(schWu, hr, "Failed to open WU service."); | ||
| 444 | |||
| 445 | if (!::QueryServiceStatus(schWu, &serviceStatus) ) | ||
| 446 | { | ||
| 447 | ExitWithLastError(hr, "Failed to query status of WU service."); | ||
| 448 | } | ||
| 449 | |||
| 450 | // Stop service if requested to. | ||
| 451 | if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService) | ||
| 452 | { | ||
| 453 | hr = StopWUService(schWu); | ||
| 454 | } | ||
| 455 | |||
| 456 | // If the service is not running then it might be disabled so let's check. | ||
| 457 | if (SERVICE_RUNNING != serviceStatus.dwCurrentState) | ||
| 458 | { | ||
| 459 | hr = SvcQueryConfig(schWu, &pConfig); | ||
| 460 | ExitOnFailure(hr, "Failed to read configuration for WU service."); | ||
| 461 | |||
| 462 | // If WU is disabled, change it to a demand start service (but touch nothing else). | ||
| 463 | if (SERVICE_DISABLED == pConfig->dwStartType) | ||
| 464 | { | ||
| 465 | hr = SetServiceStartType(schWu, SERVICE_DEMAND_START); | ||
| 466 | ExitOnFailure(hr, "Failed to mark WU service to start on demand."); | ||
| 467 | |||
| 468 | *pfPreviouslyDisabled = TRUE; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | *pschWu = schWu; | ||
| 473 | schWu = NULL; | ||
| 474 | |||
| 475 | LExit: | ||
| 476 | ReleaseMem(pConfig); | ||
| 477 | ReleaseServiceHandle(schWu); | ||
| 478 | ReleaseServiceHandle(schSCM); | ||
| 479 | |||
| 480 | return hr; | ||
| 481 | } | ||
| 482 | |||
| 483 | static HRESULT SetServiceStartType( | ||
| 484 | __in SC_HANDLE sch, | ||
| 485 | __in DWORD startType | ||
| 486 | ) | ||
| 487 | { | ||
| 488 | HRESULT hr = S_OK; | ||
| 489 | |||
| 490 | if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) | ||
| 491 | { | ||
| 492 | ExitWithLastError(hr, "Failed to set service start type."); | ||
| 493 | } | ||
| 494 | |||
| 495 | LExit: | ||
| 496 | return hr; | ||
| 497 | } | ||
| 498 | |||
| 499 | static HRESULT StopWUService( | ||
| 500 | __in SC_HANDLE schWu | ||
| 501 | ) | ||
| 502 | { | ||
| 503 | HRESULT hr = S_OK; | ||
| 504 | SERVICE_STATUS serviceStatus = { }; | ||
| 505 | |||
| 506 | if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus)) | ||
| 507 | { | ||
| 508 | ExitWithLastError(hr, "Failed to stop wusa service."); | ||
| 509 | } | ||
| 510 | |||
| 511 | LExit: | ||
| 512 | return hr; | ||
| 513 | } | ||
diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h new file mode 100644 index 00000000..d0323b06 --- /dev/null +++ b/src/engine/msuengine.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // function declarations | ||
| 11 | |||
| 12 | HRESULT MsuEngineParsePackageFromXml( | ||
| 13 | __in IXMLDOMNode* pixnMsiPackage, | ||
| 14 | __in BURN_PACKAGE* pPackage | ||
| 15 | ); | ||
| 16 | void MsuEnginePackageUninitialize( | ||
| 17 | __in BURN_PACKAGE* pPackage | ||
| 18 | ); | ||
| 19 | HRESULT MsuEngineDetectPackage( | ||
| 20 | __in BURN_PACKAGE* pPackage, | ||
| 21 | __in BURN_VARIABLES* pVariables | ||
| 22 | ); | ||
| 23 | HRESULT MsuEnginePlanCalculatePackage( | ||
| 24 | __in BURN_PACKAGE* pPackage, | ||
| 25 | __out_opt BOOL* pfBARequestedCache | ||
| 26 | ); | ||
| 27 | HRESULT MsuEnginePlanAddPackage( | ||
| 28 | __in BURN_PACKAGE* pPackage, | ||
| 29 | __in BURN_PLAN* pPlan, | ||
| 30 | __in BURN_LOGGING* pLog, | ||
| 31 | __in BURN_VARIABLES* pVariables, | ||
| 32 | __in HANDLE hCacheEvent, | ||
| 33 | __in BOOL fPlanPackageCacheRollback | ||
| 34 | ); | ||
| 35 | HRESULT MsuEngineExecutePackage( | ||
| 36 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
| 37 | __in BURN_VARIABLES* pVariables, | ||
| 38 | __in BOOL fRollback, | ||
| 39 | __in BOOL fStopWusaService, | ||
| 40 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 41 | __in LPVOID pvContext, | ||
| 42 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
| 43 | ); | ||
| 44 | |||
| 45 | |||
| 46 | #if defined(__cplusplus) | ||
| 47 | } | ||
| 48 | #endif | ||
diff --git a/src/engine/netfxchainer.cpp b/src/engine/netfxchainer.cpp new file mode 100644 index 00000000..4e7a7720 --- /dev/null +++ b/src/engine/netfxchainer.cpp | |||
| @@ -0,0 +1,418 @@ | |||
| 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 | |||
| 5 | static VOID DestroyNetFxChainer( | ||
| 6 | __in NetFxChainer* pChainer | ||
| 7 | ) | ||
| 8 | { | ||
| 9 | if (pChainer) | ||
| 10 | { | ||
| 11 | ReleaseHandle(pChainer->hSection); | ||
| 12 | ReleaseHandle(pChainer->hEventChaineeSend); | ||
| 13 | ReleaseHandle(pChainer->hEventChainerSend); | ||
| 14 | ReleaseHandle(pChainer->hMutex); | ||
| 15 | |||
| 16 | if (pChainer->pData) | ||
| 17 | { | ||
| 18 | ::UnmapViewOfFile(pChainer->pData); | ||
| 19 | } | ||
| 20 | |||
| 21 | MemFree(pChainer); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | static HRESULT CreateNetFxChainer( | ||
| 26 | __in LPCWSTR wzSectionName, | ||
| 27 | __in LPCWSTR wzEventName, | ||
| 28 | __out NetFxChainer** ppChainer | ||
| 29 | ) | ||
| 30 | { | ||
| 31 | HRESULT hr = S_OK; | ||
| 32 | LPWSTR sczName = NULL; | ||
| 33 | NetFxChainer* pChainer = NULL; | ||
| 34 | |||
| 35 | pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE); | ||
| 36 | ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct."); | ||
| 37 | |||
| 38 | pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName); | ||
| 39 | ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName); | ||
| 40 | |||
| 41 | hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName); | ||
| 42 | ExitOnFailure(hr, "failed to allocate memory for event name"); | ||
| 43 | |||
| 44 | pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName); | ||
| 45 | ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName); | ||
| 46 | |||
| 47 | hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName); | ||
| 48 | ExitOnFailure(hr, "failed to allocate memory for mutex name"); | ||
| 49 | |||
| 50 | // Create the mutex, we initially own | ||
| 51 | pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName); | ||
| 52 | ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName); | ||
| 53 | |||
| 54 | pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE, | ||
| 55 | NULL, // security attributes | ||
| 56 | PAGE_READWRITE, | ||
| 57 | 0, // high-order DWORD of maximum size | ||
| 58 | NETFXDATA_SIZE, // low-order DWORD of maximum size | ||
| 59 | wzSectionName); | ||
| 60 | ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName); | ||
| 61 | |||
| 62 | pChainer->pData = reinterpret_cast<NetFxDataStructure*>(::MapViewOfFile(pChainer->hSection, | ||
| 63 | FILE_MAP_WRITE, | ||
| 64 | 0, 0, // offsets | ||
| 65 | 0 // map entire file | ||
| 66 | )); | ||
| 67 | ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName); | ||
| 68 | |||
| 69 | // Initialize the shared memory | ||
| 70 | hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName); | ||
| 71 | ExitOnFailure(hr, "failed to copy event name to shared memory structure."); | ||
| 72 | pChainer->pData->downloadFinished = false; | ||
| 73 | pChainer->pData->downloadSoFar = 0; | ||
| 74 | pChainer->pData->hrDownloadFinished = E_PENDING; | ||
| 75 | pChainer->pData->downloadAbort = false; | ||
| 76 | pChainer->pData->installFinished = false; | ||
| 77 | pChainer->pData->installSoFar = 0; | ||
| 78 | pChainer->pData->hrInstallFinished = E_PENDING; | ||
| 79 | pChainer->pData->installAbort = false; | ||
| 80 | pChainer->pData->hrInternalError = S_OK; | ||
| 81 | pChainer->pData->version = NETFXDATA_VERSION; | ||
| 82 | pChainer->pData->messageCode = 0; | ||
| 83 | pChainer->pData->messageResponse = 0; | ||
| 84 | pChainer->pData->messageDataLength = 0; | ||
| 85 | |||
| 86 | // Done with initialization, allow others to access. | ||
| 87 | ::ReleaseMutex(pChainer->hMutex); | ||
| 88 | |||
| 89 | *ppChainer = pChainer; | ||
| 90 | pChainer = NULL; | ||
| 91 | |||
| 92 | LExit: | ||
| 93 | ReleaseStr(sczName); | ||
| 94 | |||
| 95 | if (pChainer) | ||
| 96 | { | ||
| 97 | // Something failed, release the mutex and destroy the object | ||
| 98 | if (pChainer->hMutex) | ||
| 99 | { | ||
| 100 | ::ReleaseMutex(pChainer->hMutex); | ||
| 101 | } | ||
| 102 | |||
| 103 | DestroyNetFxChainer(pChainer); | ||
| 104 | } | ||
| 105 | |||
| 106 | return hr; | ||
| 107 | } | ||
| 108 | |||
| 109 | |||
| 110 | static VOID NetFxAbort( | ||
| 111 | __in NetFxChainer* pChainer | ||
| 112 | ) | ||
| 113 | { | ||
| 114 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); | ||
| 115 | |||
| 116 | pChainer->pData->downloadAbort = true; | ||
| 117 | pChainer->pData->installAbort = true; | ||
| 118 | |||
| 119 | ::ReleaseMutex(pChainer->hMutex); | ||
| 120 | |||
| 121 | ::SetEvent(pChainer->hEventChainerSend); | ||
| 122 | } | ||
| 123 | |||
| 124 | static BYTE NetFxGetProgress( | ||
| 125 | __in NetFxChainer* pChainer | ||
| 126 | ) | ||
| 127 | { | ||
| 128 | BYTE bProgress = 0; | ||
| 129 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); | ||
| 130 | |||
| 131 | bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2; | ||
| 132 | |||
| 133 | ::ReleaseMutex(pChainer->hMutex); | ||
| 134 | |||
| 135 | return bProgress; | ||
| 136 | } | ||
| 137 | |||
| 138 | static HRESULT NetFxGetMessage( | ||
| 139 | __in NetFxChainer* pChainer, | ||
| 140 | __out DWORD* pdwMessage, | ||
| 141 | __out LPVOID* ppBuffer, | ||
| 142 | __out DWORD* pdwBufferSize | ||
| 143 | ) | ||
| 144 | { | ||
| 145 | HRESULT hr = S_OK; | ||
| 146 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); | ||
| 147 | |||
| 148 | *pdwMessage = pChainer->pData->messageCode; | ||
| 149 | *ppBuffer = NULL; | ||
| 150 | *pdwBufferSize = 0; | ||
| 151 | |||
| 152 | if (NETFX_NO_MESSAGE != *pdwMessage) | ||
| 153 | { | ||
| 154 | *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE); | ||
| 155 | ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data"); | ||
| 156 | |||
| 157 | memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength); | ||
| 158 | *pdwBufferSize = pChainer->pData->messageDataLength; | ||
| 159 | } | ||
| 160 | |||
| 161 | LExit: | ||
| 162 | ::ReleaseMutex(pChainer->hMutex); | ||
| 163 | |||
| 164 | return hr; | ||
| 165 | } | ||
| 166 | |||
| 167 | static void NetFxRespond( | ||
| 168 | __in NetFxChainer* pChainer, | ||
| 169 | __in DWORD dwResponse | ||
| 170 | ) | ||
| 171 | { | ||
| 172 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); | ||
| 173 | |||
| 174 | pChainer->pData->messageCode = NETFX_NO_MESSAGE; | ||
| 175 | pChainer->pData->messageResponse = dwResponse; | ||
| 176 | if (IDCANCEL == dwResponse) | ||
| 177 | { | ||
| 178 | pChainer->pData->downloadAbort = true; | ||
| 179 | pChainer->pData->installAbort = true; | ||
| 180 | } | ||
| 181 | |||
| 182 | ::ReleaseMutex(pChainer->hMutex); | ||
| 183 | |||
| 184 | ::SetEvent(pChainer->hEventChainerSend); | ||
| 185 | } | ||
| 186 | |||
| 187 | static HRESULT NetFxGetResult( | ||
| 188 | __in NetFxChainer* pChainer, | ||
| 189 | __out HRESULT* phrInternalError | ||
| 190 | ) | ||
| 191 | { | ||
| 192 | HRESULT hr = S_OK; | ||
| 193 | ::WaitForSingleObject(pChainer->hMutex, INFINITE); | ||
| 194 | |||
| 195 | hr = pChainer->pData->hrInstallFinished; | ||
| 196 | |||
| 197 | if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed | ||
| 198 | (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted | ||
| 199 | { | ||
| 200 | hr = pChainer->pData->hrDownloadFinished; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (phrInternalError) | ||
| 204 | { | ||
| 205 | *phrInternalError = pChainer->pData->hrInternalError; | ||
| 206 | } | ||
| 207 | |||
| 208 | ::ReleaseMutex(pChainer->hMutex); | ||
| 209 | |||
| 210 | return hr; | ||
| 211 | } | ||
| 212 | |||
| 213 | static HRESULT OnNetFxFilesInUse( | ||
| 214 | __in NetFxChainer* pNetfxChainer, | ||
| 215 | __in NetFxCloseApplications* pCloseApps, | ||
| 216 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 217 | __in LPVOID pvContext | ||
| 218 | ) | ||
| 219 | { | ||
| 220 | HRESULT hr = S_OK; | ||
| 221 | DWORD cFiles = 0; | ||
| 222 | LPWSTR* rgwzFiles = NULL; | ||
| 223 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 224 | DWORD dwResponse = 0; | ||
| 225 | |||
| 226 | cFiles = pCloseApps->dwApplicationsSize; | ||
| 227 | rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE); | ||
| 228 | ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer."); | ||
| 229 | |||
| 230 | for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i) | ||
| 231 | { | ||
| 232 | rgwzFiles[i] = pCloseApps->applications[i].szName; | ||
| 233 | } | ||
| 234 | |||
| 235 | // send message | ||
| 236 | message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE; | ||
| 237 | message.dwAllowedResults = MB_ABORTRETRYIGNORE; | ||
| 238 | message.filesInUse.cFiles = cFiles; | ||
| 239 | message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles; | ||
| 240 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); | ||
| 241 | |||
| 242 | NetFxRespond(pNetfxChainer, dwResponse); | ||
| 243 | |||
| 244 | LExit: | ||
| 245 | ReleaseMem(rgwzFiles); | ||
| 246 | |||
| 247 | return hr; | ||
| 248 | } | ||
| 249 | |||
| 250 | static HRESULT OnNetFxProgress( | ||
| 251 | __in NetFxChainer* pNetfxChainer, | ||
| 252 | __in BYTE bProgress, | ||
| 253 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 254 | __in LPVOID pvContext | ||
| 255 | ) | ||
| 256 | { | ||
| 257 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 258 | DWORD dwResponse = 0; | ||
| 259 | |||
| 260 | // send message | ||
| 261 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
| 262 | message.dwAllowedResults = MB_OKCANCEL; | ||
| 263 | message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX; | ||
| 264 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); | ||
| 265 | |||
| 266 | if (IDCANCEL == dwResponse) | ||
| 267 | { | ||
| 268 | NetFxAbort(pNetfxChainer); | ||
| 269 | } | ||
| 270 | |||
| 271 | return S_OK; | ||
| 272 | } | ||
| 273 | |||
| 274 | static HRESULT OnNetFxError( | ||
| 275 | __in NetFxChainer* /*pNetfxChainer*/, | ||
| 276 | __in HRESULT hrError, | ||
| 277 | __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler, | ||
| 278 | __in LPVOID pvContext | ||
| 279 | ) | ||
| 280 | { | ||
| 281 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
| 282 | DWORD dwResponse = 0; | ||
| 283 | |||
| 284 | // send message | ||
| 285 | message.type = GENERIC_EXECUTE_MESSAGE_ERROR; | ||
| 286 | message.dwAllowedResults = MB_OK; | ||
| 287 | message.error.dwErrorCode = hrError; | ||
| 288 | message.error.wzMessage = NULL; | ||
| 289 | dwResponse = (DWORD)pfnMessageHandler(&message, pvContext); | ||
| 290 | |||
| 291 | return S_OK; | ||
| 292 | } | ||
| 293 | |||
| 294 | static HRESULT ProcessNetFxMessage( | ||
| 295 | __in NetFxChainer* pNetfxChainer, | ||
| 296 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 297 | __in LPVOID pvContext | ||
| 298 | ) | ||
| 299 | { | ||
| 300 | HRESULT hr = S_OK; | ||
| 301 | DWORD dwMessage = NETFX_NO_MESSAGE; | ||
| 302 | DWORD dwBufferSize = 0; | ||
| 303 | LPVOID pBuffer = NULL; | ||
| 304 | |||
| 305 | // send progress | ||
| 306 | hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext); | ||
| 307 | ExitOnFailure(hr, "Failed to send progress from netfx chainer."); | ||
| 308 | |||
| 309 | // Check for message | ||
| 310 | hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize); | ||
| 311 | ExitOnFailure(hr, "Failed to get message from netfx chainer."); | ||
| 312 | |||
| 313 | switch(dwMessage) | ||
| 314 | { | ||
| 315 | case NETFX_CLOSE_APPS: | ||
| 316 | hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext); | ||
| 317 | ExitOnFailure(hr, "Failed to send files in use message from netfx chainer."); | ||
| 318 | break; | ||
| 319 | |||
| 320 | default: | ||
| 321 | // No message we understand. | ||
| 322 | break; | ||
| 323 | } | ||
| 324 | |||
| 325 | LExit: | ||
| 326 | ReleaseMem(pBuffer); | ||
| 327 | |||
| 328 | return hr; | ||
| 329 | } | ||
| 330 | |||
| 331 | extern "C" HRESULT NetFxRunChainer( | ||
| 332 | __in LPCWSTR wzExecutablePath, | ||
| 333 | __in LPCWSTR wzArguments, | ||
| 334 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 335 | __in LPVOID pvContext, | ||
| 336 | __out DWORD* pdwExitCode | ||
| 337 | ) | ||
| 338 | { | ||
| 339 | HRESULT hr = S_OK; | ||
| 340 | DWORD er = 0; | ||
| 341 | WCHAR wzGuid[GUID_STRING_LENGTH]; | ||
| 342 | LPWSTR sczEventName = NULL; | ||
| 343 | LPWSTR sczSectionName = NULL; | ||
| 344 | LPWSTR sczCommand = NULL; | ||
| 345 | NetFxChainer* pNetfxChainer = NULL; | ||
| 346 | STARTUPINFOW si = { }; | ||
| 347 | PROCESS_INFORMATION pi = { }; | ||
| 348 | HRESULT hrInternalError = 0; | ||
| 349 | |||
| 350 | // Create the unique name suffix. | ||
| 351 | hr = GuidFixedCreate(wzGuid); | ||
| 352 | ExitOnRootFailure(hr, "Failed to create netfx chainer guid."); | ||
| 353 | |||
| 354 | hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid); | ||
| 355 | ExitOnFailure(hr, "Failed to allocate section name."); | ||
| 356 | |||
| 357 | hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid); | ||
| 358 | ExitOnFailure(hr, "Failed to allocate event name."); | ||
| 359 | |||
| 360 | hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer); | ||
| 361 | ExitOnFailure(hr, "Failed to create netfx chainer."); | ||
| 362 | |||
| 363 | hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName); | ||
| 364 | ExitOnFailure(hr, "Failed to allocate netfx chainer arguments."); | ||
| 365 | |||
| 366 | si.cb = sizeof(si); | ||
| 367 | if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) | ||
| 368 | { | ||
| 369 | ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath); | ||
| 370 | } | ||
| 371 | |||
| 372 | HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend }; | ||
| 373 | |||
| 374 | for (;;) | ||
| 375 | { | ||
| 376 | er = ::WaitForMultipleObjects(2, handles, FALSE, 100); | ||
| 377 | if (WAIT_OBJECT_0 == er) | ||
| 378 | { | ||
| 379 | // Process has exited | ||
| 380 | *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError); | ||
| 381 | if (E_PENDING == *pdwExitCode) | ||
| 382 | { | ||
| 383 | if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode)) | ||
| 384 | { | ||
| 385 | ExitWithLastError(hr, "Failed to get netfx return code."); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | else if (FAILED(hrInternalError)) | ||
| 389 | { | ||
| 390 | // push internal error message | ||
| 391 | OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext); | ||
| 392 | ExitOnFailure(hr, "Failed to send internal error message from netfx chainer."); | ||
| 393 | } | ||
| 394 | |||
| 395 | break; | ||
| 396 | } | ||
| 397 | else if (WAIT_OBJECT_0 + 1 == er) | ||
| 398 | { | ||
| 399 | // Chainee has notified us of a change. | ||
| 400 | hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext); | ||
| 401 | ExitOnFailure(hr, "Failed to process netfx chainer message."); | ||
| 402 | } | ||
| 403 | else if (WAIT_FAILED == er) | ||
| 404 | { | ||
| 405 | ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete"); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | LExit: | ||
| 410 | ReleaseStr(sczSectionName); | ||
| 411 | ReleaseStr(sczEventName); | ||
| 412 | StrSecureZeroFreeString(sczCommand); | ||
| 413 | DestroyNetFxChainer(pNetfxChainer); | ||
| 414 | ReleaseHandle(pi.hThread); | ||
| 415 | ReleaseHandle(pi.hProcess); | ||
| 416 | |||
| 417 | return hr; | ||
| 418 | } | ||
diff --git a/src/engine/netfxchainer.h b/src/engine/netfxchainer.h new file mode 100644 index 00000000..7d3aff1c --- /dev/null +++ b/src/engine/netfxchainer.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | struct NetFxDataStructure | ||
| 10 | { | ||
| 11 | bool downloadFinished; // download done yet? | ||
| 12 | bool installFinished; // install done yet? | ||
| 13 | bool downloadAbort; // set downloader to abort | ||
| 14 | bool installAbort; // set installer to abort | ||
| 15 | HRESULT hrDownloadFinished; // resultant HRESULT for download | ||
| 16 | HRESULT hrInstallFinished; // resultant HRESULT for install | ||
| 17 | HRESULT hrInternalError; | ||
| 18 | WCHAR szCurrentItemStep[MAX_PATH]; | ||
| 19 | BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done) | ||
| 20 | BYTE installSoFar; // install progress 0 - 255 (0 to 100% done) | ||
| 21 | WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications | ||
| 22 | |||
| 23 | BYTE version; // version of the data structure, set by chainer. | ||
| 24 | |||
| 25 | DWORD messageCode; // current message being sent by the chainee, 0 if no message is active | ||
| 26 | DWORD messageResponse; // chainer's response to current message, 0 if not yet handled | ||
| 27 | DWORD messageDataLength; // length of the m_messageData field in bytes | ||
| 28 | BYTE messageData[1]; // variable length buffer, content depends on m_messageCode | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct NetFxChainer | ||
| 32 | { | ||
| 33 | HANDLE hSection; | ||
| 34 | |||
| 35 | HANDLE hEventChaineeSend; | ||
| 36 | HANDLE hEventChainerSend; | ||
| 37 | HANDLE hMutex; | ||
| 38 | |||
| 39 | NetFxDataStructure* pData; | ||
| 40 | DWORD dwDataSize; | ||
| 41 | }; | ||
| 42 | |||
| 43 | #define NETFXDATA_SIZE 65536 | ||
| 44 | |||
| 45 | #define NETFXDATA_VERSION 1 | ||
| 46 | |||
| 47 | #define NETFX_MESSAGE(version, defaultResponse, messageCode) \ | ||
| 48 | ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF)) | ||
| 49 | #define NETFX_MESSAGE_CODE(messageId) \ | ||
| 50 | (messageId & 0xFFFF) | ||
| 51 | #define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \ | ||
| 52 | ((messageId >> 16) & 0xFF) | ||
| 53 | #define NETFX_MESSAGE_VERSION(messageId) \ | ||
| 54 | ((messageId >>24) & 0xFF) | ||
| 55 | |||
| 56 | #define NETFX_NO_MESSAGE 0 | ||
| 57 | |||
| 58 | |||
| 59 | //------------------------------------------------------------------------------ | ||
| 60 | // NETFX_CLOSE_APPS | ||
| 61 | // | ||
| 62 | // Sent by the chainee when it detects that applications are holding files in | ||
| 63 | // use. Respond to this message in order to tell the chainee to close the | ||
| 64 | // applications to prevent a reboot. | ||
| 65 | // | ||
| 66 | // pData : NetFxCloseApplications : The list of applications | ||
| 67 | // Acceptable responses: | ||
| 68 | // IDYES : Indicates that the chainee should attempt to shutdown the apps. | ||
| 69 | // If all apps do not successfully close the message may be sent again. | ||
| 70 | // IDNO : Indicates that the chainee should not attempt to close apps. | ||
| 71 | // IDRETRY : Indicates that the chainee should refresh the list of apps. | ||
| 72 | // Another NETFX_CLOSE_APPS message will be sent asynchronously with | ||
| 73 | // the new list of apps. | ||
| 74 | //------------------------------------------------------------------------------ | ||
| 75 | #define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1) | ||
| 76 | |||
| 77 | struct NetFxApplication | ||
| 78 | { | ||
| 79 | WCHAR szName[MAX_PATH]; | ||
| 80 | DWORD dwPid; | ||
| 81 | }; | ||
| 82 | |||
| 83 | struct NetFxCloseApplications | ||
| 84 | { | ||
| 85 | DWORD dwApplicationsSize; | ||
| 86 | NetFxApplication applications[1]; | ||
| 87 | }; | ||
| 88 | |||
| 89 | HRESULT NetFxRunChainer( | ||
| 90 | __in LPCWSTR wzExecutablePath, | ||
| 91 | __in LPCWSTR wzArguments, | ||
| 92 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
| 93 | __in LPVOID pvContext, | ||
| 94 | __out DWORD* pdwExitCode | ||
| 95 | ); | ||
| 96 | #if defined(__cplusplus) | ||
| 97 | } | ||
| 98 | #endif | ||
diff --git a/src/engine/package.cpp b/src/engine/package.cpp new file mode 100644 index 00000000..02958efd --- /dev/null +++ b/src/engine/package.cpp | |||
| @@ -0,0 +1,678 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | static HRESULT ParsePayloadRefsFromXml( | ||
| 9 | __in BURN_PACKAGE* pPackage, | ||
| 10 | __in BURN_PAYLOADS* pPayloads, | ||
| 11 | __in IXMLDOMNode* pixnPackage | ||
| 12 | ); | ||
| 13 | static HRESULT ParsePatchTargetCode( | ||
| 14 | __in BURN_PACKAGES* pPackages, | ||
| 15 | __in IXMLDOMNode* pixnBundle | ||
| 16 | ); | ||
| 17 | static HRESULT FindRollbackBoundaryById( | ||
| 18 | __in BURN_PACKAGES* pPackages, | ||
| 19 | __in_z LPCWSTR wzId, | ||
| 20 | __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
| 21 | ); | ||
| 22 | |||
| 23 | |||
| 24 | // function definitions | ||
| 25 | |||
| 26 | extern "C" HRESULT PackagesParseFromXml( | ||
| 27 | __in BURN_PACKAGES* pPackages, | ||
| 28 | __in BURN_PAYLOADS* pPayloads, | ||
| 29 | __in IXMLDOMNode* pixnBundle | ||
| 30 | ) | ||
| 31 | { | ||
| 32 | HRESULT hr = S_OK; | ||
| 33 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 34 | IXMLDOMNode* pixnNode = NULL; | ||
| 35 | DWORD cNodes = 0; | ||
| 36 | BSTR bstrNodeName = NULL; | ||
| 37 | DWORD cMspPackages = 0; | ||
| 38 | LPWSTR scz = NULL; | ||
| 39 | |||
| 40 | // select rollback boundary nodes | ||
| 41 | hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); | ||
| 42 | ExitOnFailure(hr, "Failed to select rollback boundary nodes."); | ||
| 43 | |||
| 44 | // get rollback boundary node count | ||
| 45 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 46 | ExitOnFailure(hr, "Failed to get rollback bundary node count."); | ||
| 47 | |||
| 48 | if (cNodes) | ||
| 49 | { | ||
| 50 | // allocate memory for rollback boundaries | ||
| 51 | pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE); | ||
| 52 | ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs."); | ||
| 53 | |||
| 54 | pPackages->cRollbackBoundaries = cNodes; | ||
| 55 | |||
| 56 | // parse rollback boundary elements | ||
| 57 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 58 | { | ||
| 59 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; | ||
| 60 | |||
| 61 | hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); | ||
| 62 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 63 | |||
| 64 | // @Id | ||
| 65 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); | ||
| 66 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 67 | |||
| 68 | // @Vital | ||
| 69 | hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); | ||
| 70 | ExitOnFailure(hr, "Failed to get @Vital."); | ||
| 71 | |||
| 72 | // @Transaction | ||
| 73 | hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction); | ||
| 74 | ExitOnFailure(hr, "Failed to get @Transaction."); | ||
| 75 | |||
| 76 | // prepare next iteration | ||
| 77 | ReleaseNullObject(pixnNode); | ||
| 78 | ReleaseNullBSTR(bstrNodeName); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. | ||
| 83 | |||
| 84 | // select package nodes | ||
| 85 | hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); | ||
| 86 | ExitOnFailure(hr, "Failed to select package nodes."); | ||
| 87 | |||
| 88 | // get package node count | ||
| 89 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 90 | ExitOnFailure(hr, "Failed to get package node count."); | ||
| 91 | |||
| 92 | if (!cNodes) | ||
| 93 | { | ||
| 94 | ExitFunction1(hr = S_OK); | ||
| 95 | } | ||
| 96 | |||
| 97 | // allocate memory for packages | ||
| 98 | pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE); | ||
| 99 | ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs."); | ||
| 100 | |||
| 101 | pPackages->cPackages = cNodes; | ||
| 102 | |||
| 103 | // parse package elements | ||
| 104 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 105 | { | ||
| 106 | BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; | ||
| 107 | |||
| 108 | hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); | ||
| 109 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 110 | |||
| 111 | // @Id | ||
| 112 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); | ||
| 113 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 114 | |||
| 115 | // @Cache | ||
| 116 | hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); | ||
| 117 | if (SUCCEEDED(hr)) | ||
| 118 | { | ||
| 119 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) | ||
| 120 | { | ||
| 121 | pPackage->cacheType = BURN_CACHE_TYPE_NO; | ||
| 122 | } | ||
| 123 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) | ||
| 124 | { | ||
| 125 | pPackage->cacheType = BURN_CACHE_TYPE_YES; | ||
| 126 | } | ||
| 127 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"always", -1)) | ||
| 128 | { | ||
| 129 | pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS; | ||
| 130 | } | ||
| 131 | else | ||
| 132 | { | ||
| 133 | hr = E_UNEXPECTED; | ||
| 134 | ExitOnFailure(hr, "Invalid cache type: %ls", scz); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | ExitOnFailure(hr, "Failed to get @Cache."); | ||
| 138 | |||
| 139 | // @CacheId | ||
| 140 | hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); | ||
| 141 | ExitOnFailure(hr, "Failed to get @CacheId."); | ||
| 142 | |||
| 143 | // @Size | ||
| 144 | hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize); | ||
| 145 | ExitOnFailure(hr, "Failed to get @Size."); | ||
| 146 | |||
| 147 | // @InstallSize | ||
| 148 | hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize); | ||
| 149 | ExitOnFailure(hr, "Failed to get @InstallSize."); | ||
| 150 | |||
| 151 | // @PerMachine | ||
| 152 | hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); | ||
| 153 | ExitOnFailure(hr, "Failed to get @PerMachine."); | ||
| 154 | |||
| 155 | // @Permanent | ||
| 156 | hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable); | ||
| 157 | ExitOnFailure(hr, "Failed to get @Permanent."); | ||
| 158 | pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable. | ||
| 159 | |||
| 160 | // @Vital | ||
| 161 | hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); | ||
| 162 | ExitOnFailure(hr, "Failed to get @Vital."); | ||
| 163 | |||
| 164 | // @LogPathVariable | ||
| 165 | hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); | ||
| 166 | if (E_NOTFOUND != hr) | ||
| 167 | { | ||
| 168 | ExitOnFailure(hr, "Failed to get @LogPathVariable."); | ||
| 169 | } | ||
| 170 | |||
| 171 | // @RollbackLogPathVariable | ||
| 172 | hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); | ||
| 173 | if (E_NOTFOUND != hr) | ||
| 174 | { | ||
| 175 | ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable."); | ||
| 176 | } | ||
| 177 | |||
| 178 | // @InstallCondition | ||
| 179 | hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); | ||
| 180 | if (E_NOTFOUND != hr) | ||
| 181 | { | ||
| 182 | ExitOnFailure(hr, "Failed to get @InstallCondition."); | ||
| 183 | } | ||
| 184 | |||
| 185 | // @RollbackBoundaryForward | ||
| 186 | hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); | ||
| 187 | if (E_NOTFOUND != hr) | ||
| 188 | { | ||
| 189 | ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward."); | ||
| 190 | |||
| 191 | hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); | ||
| 192 | ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); | ||
| 193 | } | ||
| 194 | |||
| 195 | // @RollbackBoundaryBackward | ||
| 196 | hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); | ||
| 197 | if (E_NOTFOUND != hr) | ||
| 198 | { | ||
| 199 | ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward."); | ||
| 200 | |||
| 201 | hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); | ||
| 202 | ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); | ||
| 203 | } | ||
| 204 | |||
| 205 | // read type specific attributes | ||
| 206 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) | ||
| 207 | { | ||
| 208 | pPackage->type = BURN_PACKAGE_TYPE_EXE; | ||
| 209 | |||
| 210 | hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization | ||
| 211 | ExitOnFailure(hr, "Failed to parse EXE package."); | ||
| 212 | } | ||
| 213 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1)) | ||
| 214 | { | ||
| 215 | pPackage->type = BURN_PACKAGE_TYPE_MSI; | ||
| 216 | |||
| 217 | hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization | ||
| 218 | ExitOnFailure(hr, "Failed to parse MSI package."); | ||
| 219 | } | ||
| 220 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1)) | ||
| 221 | { | ||
| 222 | pPackage->type = BURN_PACKAGE_TYPE_MSP; | ||
| 223 | |||
| 224 | hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization | ||
| 225 | ExitOnFailure(hr, "Failed to parse MSP package."); | ||
| 226 | |||
| 227 | ++cMspPackages; | ||
| 228 | } | ||
| 229 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1)) | ||
| 230 | { | ||
| 231 | pPackage->type = BURN_PACKAGE_TYPE_MSU; | ||
| 232 | |||
| 233 | hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization | ||
| 234 | ExitOnFailure(hr, "Failed to parse MSU package."); | ||
| 235 | } | ||
| 236 | else | ||
| 237 | { | ||
| 238 | // ignore other package types for now | ||
| 239 | } | ||
| 240 | |||
| 241 | // parse payload references | ||
| 242 | hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode); | ||
| 243 | ExitOnFailure(hr, "Failed to parse payload references."); | ||
| 244 | |||
| 245 | // parse dependency providers | ||
| 246 | hr = DependencyParseProvidersFromXml(pPackage, pixnNode); | ||
| 247 | ExitOnFailure(hr, "Failed to parse dependency providers."); | ||
| 248 | |||
| 249 | // prepare next iteration | ||
| 250 | ReleaseNullObject(pixnNode); | ||
| 251 | ReleaseNullBSTR(bstrNodeName); | ||
| 252 | } | ||
| 253 | |||
| 254 | if (cMspPackages) | ||
| 255 | { | ||
| 256 | pPackages->rgPatchInfo = static_cast<MSIPATCHSEQUENCEINFOW*>(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE)); | ||
| 257 | ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information."); | ||
| 258 | |||
| 259 | pPackages->rgPatchInfoToPackage = static_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE)); | ||
| 260 | ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup."); | ||
| 261 | |||
| 262 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 263 | { | ||
| 264 | BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; | ||
| 265 | |||
| 266 | if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 267 | { | ||
| 268 | pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml; | ||
| 269 | pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB; | ||
| 270 | pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage; | ||
| 271 | ++pPackages->cPatchInfo; | ||
| 272 | |||
| 273 | // Loop through all MSI packages seeing if any of them slipstream this MSP. | ||
| 274 | for (DWORD j = 0; j < pPackages->cPackages; ++j) | ||
| 275 | { | ||
| 276 | BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j]; | ||
| 277 | |||
| 278 | if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) | ||
| 279 | { | ||
| 280 | for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k) | ||
| 281 | { | ||
| 282 | if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) | ||
| 283 | { | ||
| 284 | pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage; | ||
| 285 | |||
| 286 | ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); | ||
| 296 | |||
| 297 | hr = ParsePatchTargetCode(pPackages, pixnBundle); | ||
| 298 | ExitOnFailure(hr, "Failed to parse target product codes."); | ||
| 299 | |||
| 300 | hr = S_OK; | ||
| 301 | |||
| 302 | LExit: | ||
| 303 | ReleaseObject(pixnNodes); | ||
| 304 | ReleaseObject(pixnNode); | ||
| 305 | ReleaseBSTR(bstrNodeName); | ||
| 306 | ReleaseStr(scz); | ||
| 307 | |||
| 308 | return hr; | ||
| 309 | } | ||
| 310 | |||
| 311 | extern "C" void PackageUninitialize( | ||
| 312 | __in BURN_PACKAGE* pPackage | ||
| 313 | ) | ||
| 314 | { | ||
| 315 | ReleaseStr(pPackage->sczId); | ||
| 316 | ReleaseStr(pPackage->sczLogPathVariable); | ||
| 317 | ReleaseStr(pPackage->sczRollbackLogPathVariable); | ||
| 318 | ReleaseStr(pPackage->sczInstallCondition); | ||
| 319 | ReleaseStr(pPackage->sczRollbackInstallCondition); | ||
| 320 | ReleaseStr(pPackage->sczCacheId); | ||
| 321 | |||
| 322 | if (pPackage->rgDependencyProviders) | ||
| 323 | { | ||
| 324 | for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) | ||
| 325 | { | ||
| 326 | DependencyUninitialize(pPackage->rgDependencyProviders + i); | ||
| 327 | } | ||
| 328 | MemFree(pPackage->rgDependencyProviders); | ||
| 329 | } | ||
| 330 | |||
| 331 | ReleaseMem(pPackage->rgPayloads); | ||
| 332 | |||
| 333 | switch (pPackage->type) | ||
| 334 | { | ||
| 335 | case BURN_PACKAGE_TYPE_EXE: | ||
| 336 | ExeEnginePackageUninitialize(pPackage); // TODO: Modularization | ||
| 337 | break; | ||
| 338 | case BURN_PACKAGE_TYPE_MSI: | ||
| 339 | MsiEnginePackageUninitialize(pPackage); // TODO: Modularization | ||
| 340 | break; | ||
| 341 | case BURN_PACKAGE_TYPE_MSP: | ||
| 342 | MspEnginePackageUninitialize(pPackage); // TODO: Modularization | ||
| 343 | break; | ||
| 344 | case BURN_PACKAGE_TYPE_MSU: | ||
| 345 | MsuEnginePackageUninitialize(pPackage); // TODO: Modularization | ||
| 346 | break; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | extern "C" void PackagesUninitialize( | ||
| 351 | __in BURN_PACKAGES* pPackages | ||
| 352 | ) | ||
| 353 | { | ||
| 354 | if (pPackages->rgRollbackBoundaries) | ||
| 355 | { | ||
| 356 | for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) | ||
| 357 | { | ||
| 358 | ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); | ||
| 359 | } | ||
| 360 | MemFree(pPackages->rgRollbackBoundaries); | ||
| 361 | } | ||
| 362 | |||
| 363 | if (pPackages->rgPackages) | ||
| 364 | { | ||
| 365 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 366 | { | ||
| 367 | PackageUninitialize(pPackages->rgPackages + i); | ||
| 368 | } | ||
| 369 | MemFree(pPackages->rgPackages); | ||
| 370 | } | ||
| 371 | |||
| 372 | if (pPackages->rgCompatiblePackages) | ||
| 373 | { | ||
| 374 | for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) | ||
| 375 | { | ||
| 376 | PackageUninitialize(pPackages->rgCompatiblePackages + i); | ||
| 377 | } | ||
| 378 | MemFree(pPackages->rgCompatiblePackages); | ||
| 379 | } | ||
| 380 | |||
| 381 | if (pPackages->rgPatchTargetCodes) | ||
| 382 | { | ||
| 383 | for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) | ||
| 384 | { | ||
| 385 | ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode); | ||
| 386 | } | ||
| 387 | MemFree(pPackages->rgPatchTargetCodes); | ||
| 388 | } | ||
| 389 | |||
| 390 | ReleaseMem(pPackages->rgPatchInfo); | ||
| 391 | ReleaseMem(pPackages->rgPatchInfoToPackage); | ||
| 392 | |||
| 393 | // clear struct | ||
| 394 | memset(pPackages, 0, sizeof(BURN_PACKAGES)); | ||
| 395 | } | ||
| 396 | |||
| 397 | extern "C" HRESULT PackageFindById( | ||
| 398 | __in BURN_PACKAGES* pPackages, | ||
| 399 | __in_z LPCWSTR wzId, | ||
| 400 | __out BURN_PACKAGE** ppPackage | ||
| 401 | ) | ||
| 402 | { | ||
| 403 | HRESULT hr = S_OK; | ||
| 404 | BURN_PACKAGE* pPackage = NULL; | ||
| 405 | |||
| 406 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 407 | { | ||
| 408 | pPackage = &pPackages->rgPackages[i]; | ||
| 409 | |||
| 410 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) | ||
| 411 | { | ||
| 412 | *ppPackage = pPackage; | ||
| 413 | ExitFunction1(hr = S_OK); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) | ||
| 418 | { | ||
| 419 | pPackage = &pPackages->rgCompatiblePackages[i]; | ||
| 420 | |||
| 421 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) | ||
| 422 | { | ||
| 423 | *ppPackage = pPackage; | ||
| 424 | ExitFunction1(hr = S_OK); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | hr = E_NOTFOUND; | ||
| 429 | |||
| 430 | LExit: | ||
| 431 | return hr; | ||
| 432 | } | ||
| 433 | |||
| 434 | |||
| 435 | extern "C" HRESULT PackageFindRelatedById( | ||
| 436 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 437 | __in_z LPCWSTR wzId, | ||
| 438 | __out BURN_PACKAGE** ppPackage | ||
| 439 | ) | ||
| 440 | { | ||
| 441 | HRESULT hr = S_OK; | ||
| 442 | BURN_PACKAGE* pPackage = NULL; | ||
| 443 | |||
| 444 | for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) | ||
| 445 | { | ||
| 446 | pPackage = &pRelatedBundles->rgRelatedBundles[i].package; | ||
| 447 | |||
| 448 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) | ||
| 449 | { | ||
| 450 | *ppPackage = pPackage; | ||
| 451 | ExitFunction1(hr = S_OK); | ||
| 452 | } | ||
| 453 | } | ||
| 454 | |||
| 455 | hr = E_NOTFOUND; | ||
| 456 | |||
| 457 | LExit: | ||
| 458 | return hr; | ||
| 459 | } | ||
| 460 | |||
| 461 | /******************************************************************** | ||
| 462 | PackageGetProperty - Determines if the property is defined | ||
| 463 | and optionally copies the property value. | ||
| 464 | |||
| 465 | Note: The caller must free psczValue if requested. | ||
| 466 | |||
| 467 | Note: Returns E_NOTFOUND if the property was not defined or if the | ||
| 468 | package does not support properties. | ||
| 469 | |||
| 470 | *********************************************************************/ | ||
| 471 | extern "C" HRESULT PackageGetProperty( | ||
| 472 | __in const BURN_PACKAGE* pPackage, | ||
| 473 | __in_z LPCWSTR wzProperty, | ||
| 474 | __out_z_opt LPWSTR* psczValue | ||
| 475 | ) | ||
| 476 | { | ||
| 477 | HRESULT hr = E_NOTFOUND; | ||
| 478 | BURN_MSIPROPERTY* rgProperties = NULL; | ||
| 479 | DWORD cProperties = 0; | ||
| 480 | |||
| 481 | // For MSIs and MSPs, enumerate the properties looking for wzProperty. | ||
| 482 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type) | ||
| 483 | { | ||
| 484 | rgProperties = pPackage->Msi.rgProperties; | ||
| 485 | cProperties = pPackage->Msi.cProperties; | ||
| 486 | } | ||
| 487 | else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 488 | { | ||
| 489 | rgProperties = pPackage->Msp.rgProperties; | ||
| 490 | cProperties = pPackage->Msp.cProperties; | ||
| 491 | } | ||
| 492 | |||
| 493 | for (DWORD i = 0; i < cProperties; ++i) | ||
| 494 | { | ||
| 495 | const BURN_MSIPROPERTY* pProperty = &rgProperties[i]; | ||
| 496 | |||
| 497 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1)) | ||
| 498 | { | ||
| 499 | if (psczValue) | ||
| 500 | { | ||
| 501 | hr = StrAllocString(psczValue, pProperty->sczValue, 0); | ||
| 502 | ExitOnFailure(hr, "Failed to copy the property value."); | ||
| 503 | } | ||
| 504 | |||
| 505 | ExitFunction1(hr = S_OK); | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | LExit: | ||
| 510 | return hr; | ||
| 511 | } | ||
| 512 | |||
| 513 | HRESULT PackageEnsureCompatiblePackagesArray( | ||
| 514 | __in BURN_PACKAGES* pPackages | ||
| 515 | ) | ||
| 516 | { | ||
| 517 | HRESULT hr = S_OK; | ||
| 518 | |||
| 519 | if (!pPackages->rgCompatiblePackages) | ||
| 520 | { | ||
| 521 | pPackages->rgCompatiblePackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * pPackages->cPackages, TRUE); | ||
| 522 | ExitOnNull(pPackages->rgCompatiblePackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for compatible packages."); | ||
| 523 | } | ||
| 524 | |||
| 525 | LExit: | ||
| 526 | return hr; | ||
| 527 | } | ||
| 528 | |||
| 529 | |||
| 530 | // internal function declarations | ||
| 531 | |||
| 532 | static HRESULT ParsePayloadRefsFromXml( | ||
| 533 | __in BURN_PACKAGE* pPackage, | ||
| 534 | __in BURN_PAYLOADS* pPayloads, | ||
| 535 | __in IXMLDOMNode* pixnPackage | ||
| 536 | ) | ||
| 537 | { | ||
| 538 | HRESULT hr = S_OK; | ||
| 539 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 540 | IXMLDOMNode* pixnNode = NULL; | ||
| 541 | DWORD cNodes = 0; | ||
| 542 | LPWSTR sczId = NULL; | ||
| 543 | |||
| 544 | // select package nodes | ||
| 545 | hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes); | ||
| 546 | ExitOnFailure(hr, "Failed to select package nodes."); | ||
| 547 | |||
| 548 | // get package node count | ||
| 549 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 550 | ExitOnFailure(hr, "Failed to get package node count."); | ||
| 551 | |||
| 552 | if (!cNodes) | ||
| 553 | { | ||
| 554 | ExitFunction1(hr = S_OK); | ||
| 555 | } | ||
| 556 | |||
| 557 | // allocate memory for payload pointers | ||
| 558 | pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD*)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * cNodes, TRUE); | ||
| 559 | ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); | ||
| 560 | |||
| 561 | pPackage->cPayloads = cNodes; | ||
| 562 | |||
| 563 | // parse package elements | ||
| 564 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 565 | { | ||
| 566 | BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; | ||
| 567 | |||
| 568 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 569 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 570 | |||
| 571 | // @Id | ||
| 572 | hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); | ||
| 573 | ExitOnFailure(hr, "Failed to get Id attribute."); | ||
| 574 | |||
| 575 | // find payload | ||
| 576 | hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); | ||
| 577 | ExitOnFailure(hr, "Failed to find payload."); | ||
| 578 | |||
| 579 | // prepare next iteration | ||
| 580 | ReleaseNullObject(pixnNode); | ||
| 581 | } | ||
| 582 | |||
| 583 | hr = S_OK; | ||
| 584 | |||
| 585 | LExit: | ||
| 586 | ReleaseObject(pixnNodes); | ||
| 587 | ReleaseObject(pixnNode); | ||
| 588 | ReleaseStr(sczId); | ||
| 589 | |||
| 590 | return hr; | ||
| 591 | } | ||
| 592 | |||
| 593 | static HRESULT ParsePatchTargetCode( | ||
| 594 | __in BURN_PACKAGES* pPackages, | ||
| 595 | __in IXMLDOMNode* pixnBundle | ||
| 596 | ) | ||
| 597 | { | ||
| 598 | HRESULT hr = S_OK; | ||
| 599 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 600 | IXMLDOMNode* pixnNode = NULL; | ||
| 601 | DWORD cNodes = 0; | ||
| 602 | BSTR bstrNodeText = NULL; | ||
| 603 | BOOL fProduct; | ||
| 604 | |||
| 605 | hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); | ||
| 606 | ExitOnFailure(hr, "Failed to select PatchTargetCode nodes."); | ||
| 607 | |||
| 608 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 609 | ExitOnFailure(hr, "Failed to get PatchTargetCode node count."); | ||
| 610 | |||
| 611 | if (!cNodes) | ||
| 612 | { | ||
| 613 | ExitFunction1(hr = S_OK); | ||
| 614 | } | ||
| 615 | |||
| 616 | pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE); | ||
| 617 | ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes."); | ||
| 618 | |||
| 619 | pPackages->cPatchTargetCodes = cNodes; | ||
| 620 | |||
| 621 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 622 | { | ||
| 623 | BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; | ||
| 624 | |||
| 625 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 626 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 627 | |||
| 628 | hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode); | ||
| 629 | ExitOnFailure(hr, "Failed to get @TargetCode attribute."); | ||
| 630 | |||
| 631 | hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct); | ||
| 632 | if (E_NOTFOUND == hr) | ||
| 633 | { | ||
| 634 | fProduct = FALSE; | ||
| 635 | hr = S_OK; | ||
| 636 | } | ||
| 637 | ExitOnFailure(hr, "Failed to get @Product."); | ||
| 638 | |||
| 639 | pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; | ||
| 640 | |||
| 641 | // prepare next iteration | ||
| 642 | ReleaseNullBSTR(bstrNodeText); | ||
| 643 | ReleaseNullObject(pixnNode); | ||
| 644 | } | ||
| 645 | |||
| 646 | LExit: | ||
| 647 | ReleaseBSTR(bstrNodeText); | ||
| 648 | ReleaseObject(pixnNode); | ||
| 649 | ReleaseObject(pixnNodes); | ||
| 650 | |||
| 651 | return hr; | ||
| 652 | } | ||
| 653 | |||
| 654 | static HRESULT FindRollbackBoundaryById( | ||
| 655 | __in BURN_PACKAGES* pPackages, | ||
| 656 | __in_z LPCWSTR wzId, | ||
| 657 | __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
| 658 | ) | ||
| 659 | { | ||
| 660 | HRESULT hr = S_OK; | ||
| 661 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
| 662 | |||
| 663 | for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) | ||
| 664 | { | ||
| 665 | pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; | ||
| 666 | |||
| 667 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) | ||
| 668 | { | ||
| 669 | *ppRollbackBoundary = pRollbackBoundary; | ||
| 670 | ExitFunction1(hr = S_OK); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | hr = E_NOTFOUND; | ||
| 675 | |||
| 676 | LExit: | ||
| 677 | return hr; | ||
| 678 | } | ||
diff --git a/src/engine/package.h b/src/engine/package.h new file mode 100644 index 00000000..c295378e --- /dev/null +++ b/src/engine/package.h | |||
| @@ -0,0 +1,336 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | struct _BURN_RELATED_BUNDLES; | ||
| 10 | typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; | ||
| 11 | |||
| 12 | struct _BURN_PACKAGE; | ||
| 13 | typedef _BURN_PACKAGE BURN_PACKAGE; | ||
| 14 | |||
| 15 | // constants | ||
| 16 | |||
| 17 | enum BURN_EXE_EXIT_CODE_TYPE | ||
| 18 | { | ||
| 19 | BURN_EXE_EXIT_CODE_TYPE_NONE, | ||
| 20 | BURN_EXE_EXIT_CODE_TYPE_SUCCESS, | ||
| 21 | BURN_EXE_EXIT_CODE_TYPE_ERROR, | ||
| 22 | BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT, | ||
| 23 | BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT, | ||
| 24 | }; | ||
| 25 | |||
| 26 | enum BURN_EXE_PROTOCOL_TYPE | ||
| 27 | { | ||
| 28 | BURN_EXE_PROTOCOL_TYPE_NONE, | ||
| 29 | BURN_EXE_PROTOCOL_TYPE_BURN, | ||
| 30 | BURN_EXE_PROTOCOL_TYPE_NETFX4, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum BURN_PACKAGE_TYPE | ||
| 34 | { | ||
| 35 | BURN_PACKAGE_TYPE_NONE, | ||
| 36 | BURN_PACKAGE_TYPE_EXE, | ||
| 37 | BURN_PACKAGE_TYPE_MSI, | ||
| 38 | BURN_PACKAGE_TYPE_MSP, | ||
| 39 | BURN_PACKAGE_TYPE_MSU, | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum BURN_CACHE_STATE | ||
| 43 | { | ||
| 44 | BURN_CACHE_STATE_NONE, | ||
| 45 | BURN_CACHE_STATE_PARTIAL, | ||
| 46 | BURN_CACHE_STATE_COMPLETE, | ||
| 47 | }; | ||
| 48 | |||
| 49 | enum BURN_CACHE_TYPE | ||
| 50 | { | ||
| 51 | BURN_CACHE_TYPE_NO, | ||
| 52 | BURN_CACHE_TYPE_YES, | ||
| 53 | BURN_CACHE_TYPE_ALWAYS, | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum BURN_DEPENDENCY_ACTION | ||
| 57 | { | ||
| 58 | BURN_DEPENDENCY_ACTION_NONE, | ||
| 59 | BURN_DEPENDENCY_ACTION_REGISTER, | ||
| 60 | BURN_DEPENDENCY_ACTION_UNREGISTER, | ||
| 61 | }; | ||
| 62 | |||
| 63 | enum BURN_PATCH_TARGETCODE_TYPE | ||
| 64 | { | ||
| 65 | BURN_PATCH_TARGETCODE_TYPE_UNKNOWN, | ||
| 66 | BURN_PATCH_TARGETCODE_TYPE_PRODUCT, | ||
| 67 | BURN_PATCH_TARGETCODE_TYPE_UPGRADE, | ||
| 68 | }; | ||
| 69 | |||
| 70 | // structs | ||
| 71 | |||
| 72 | typedef struct _BURN_EXE_EXIT_CODE | ||
| 73 | { | ||
| 74 | BURN_EXE_EXIT_CODE_TYPE type; | ||
| 75 | DWORD dwCode; | ||
| 76 | BOOL fWildcard; | ||
| 77 | } BURN_EXE_EXIT_CODE; | ||
| 78 | |||
| 79 | typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT | ||
| 80 | { | ||
| 81 | LPWSTR sczInstallArgument; | ||
| 82 | LPWSTR sczUninstallArgument; | ||
| 83 | LPWSTR sczRepairArgument; | ||
| 84 | LPWSTR sczCondition; | ||
| 85 | } BURN_EXE_COMMAND_LINE_ARGUMENT; | ||
| 86 | |||
| 87 | typedef struct _BURN_MSPTARGETPRODUCT | ||
| 88 | { | ||
| 89 | MSIINSTALLCONTEXT context; | ||
| 90 | DWORD dwOrder; | ||
| 91 | WCHAR wzTargetProductCode[39]; | ||
| 92 | BURN_PACKAGE* pChainedTargetPackage; | ||
| 93 | BOOL fSlipstream; | ||
| 94 | |||
| 95 | BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect. | ||
| 96 | BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. | ||
| 97 | BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. | ||
| 98 | } BURN_MSPTARGETPRODUCT; | ||
| 99 | |||
| 100 | typedef struct _BURN_MSIPROPERTY | ||
| 101 | { | ||
| 102 | LPWSTR sczId; | ||
| 103 | LPWSTR sczValue; // used during forward execution | ||
| 104 | LPWSTR sczRollbackValue; // used during rollback | ||
| 105 | LPWSTR sczCondition; | ||
| 106 | } BURN_MSIPROPERTY; | ||
| 107 | |||
| 108 | typedef struct _BURN_MSIFEATURE | ||
| 109 | { | ||
| 110 | LPWSTR sczId; | ||
| 111 | LPWSTR sczAddLocalCondition; | ||
| 112 | LPWSTR sczAddSourceCondition; | ||
| 113 | LPWSTR sczAdvertiseCondition; | ||
| 114 | LPWSTR sczRollbackAddLocalCondition; | ||
| 115 | LPWSTR sczRollbackAddSourceCondition; | ||
| 116 | LPWSTR sczRollbackAdvertiseCondition; | ||
| 117 | |||
| 118 | BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect. | ||
| 119 | BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan. | ||
| 120 | BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. | ||
| 121 | } BURN_MSIFEATURE; | ||
| 122 | |||
| 123 | typedef struct _BURN_RELATED_MSI | ||
| 124 | { | ||
| 125 | LPWSTR sczUpgradeCode; | ||
| 126 | DWORD64 qwMinVersion; | ||
| 127 | DWORD64 qwMaxVersion; | ||
| 128 | BOOL fMinProvided; | ||
| 129 | BOOL fMaxProvided; | ||
| 130 | BOOL fMinInclusive; | ||
| 131 | BOOL fMaxInclusive; | ||
| 132 | BOOL fOnlyDetect; | ||
| 133 | BOOL fLangInclusive; | ||
| 134 | |||
| 135 | DWORD* rgdwLanguages; | ||
| 136 | DWORD cLanguages; | ||
| 137 | } BURN_RELATED_MSI; | ||
| 138 | |||
| 139 | typedef struct _BURN_PACKAGE_PAYLOAD | ||
| 140 | { | ||
| 141 | BURN_PAYLOAD* pPayload; | ||
| 142 | BOOL fCached; | ||
| 143 | } BURN_PACKAGE_PAYLOAD; | ||
| 144 | |||
| 145 | typedef struct _BURN_DEPENDENCY_PROVIDER | ||
| 146 | { | ||
| 147 | LPWSTR sczKey; | ||
| 148 | LPWSTR sczVersion; | ||
| 149 | LPWSTR sczDisplayName; | ||
| 150 | BOOL fImported; | ||
| 151 | } BURN_DEPENDENCY_PROVIDER; | ||
| 152 | |||
| 153 | typedef struct _BURN_ROLLBACK_BOUNDARY | ||
| 154 | { | ||
| 155 | LPWSTR sczId; | ||
| 156 | BOOL fVital; | ||
| 157 | BOOL fTransaction; | ||
| 158 | } BURN_ROLLBACK_BOUNDARY; | ||
| 159 | |||
| 160 | typedef struct _BURN_PATCH_TARGETCODE | ||
| 161 | { | ||
| 162 | LPWSTR sczTargetCode; | ||
| 163 | BURN_PATCH_TARGETCODE_TYPE type; | ||
| 164 | } BURN_PATCH_TARGETCODE; | ||
| 165 | |||
| 166 | typedef struct _BURN_PACKAGE | ||
| 167 | { | ||
| 168 | LPWSTR sczId; | ||
| 169 | |||
| 170 | LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path. | ||
| 171 | LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path. | ||
| 172 | |||
| 173 | LPWSTR sczInstallCondition; | ||
| 174 | LPWSTR sczRollbackInstallCondition; | ||
| 175 | BOOL fPerMachine; | ||
| 176 | BOOL fUninstallable; | ||
| 177 | BOOL fVital; | ||
| 178 | |||
| 179 | BURN_CACHE_TYPE cacheType; | ||
| 180 | LPWSTR sczCacheId; | ||
| 181 | |||
| 182 | DWORD64 qwInstallSize; | ||
| 183 | DWORD64 qwSize; | ||
| 184 | |||
| 185 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. | ||
| 186 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. | ||
| 187 | |||
| 188 | BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. | ||
| 189 | BURN_CACHE_STATE cache; // only valid after Detect. | ||
| 190 | BOOTSTRAPPER_PACKAGE_STATE expected; // only valid during Plan. | ||
| 191 | BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan. | ||
| 192 | BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan. | ||
| 193 | BOOL fAcquire; // only valid during Plan. | ||
| 194 | BOOL fUncache; // only valid during Plan. | ||
| 195 | BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan. | ||
| 196 | BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan. | ||
| 197 | BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan. | ||
| 198 | BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan. | ||
| 199 | BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan. | ||
| 200 | BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan. | ||
| 201 | BOOL fDependencyManagerWasHere; // only valid during Plan. | ||
| 202 | HRESULT hrCacheResult; // only valid during Apply. | ||
| 203 | |||
| 204 | BURN_PACKAGE_PAYLOAD* rgPayloads; | ||
| 205 | DWORD cPayloads; | ||
| 206 | |||
| 207 | BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; | ||
| 208 | DWORD cDependencyProviders; | ||
| 209 | |||
| 210 | BURN_PACKAGE_TYPE type; | ||
| 211 | union | ||
| 212 | { | ||
| 213 | struct | ||
| 214 | { | ||
| 215 | LPWSTR sczDetectCondition; | ||
| 216 | LPWSTR sczInstallArguments; | ||
| 217 | LPWSTR sczRepairArguments; | ||
| 218 | LPWSTR sczUninstallArguments; | ||
| 219 | LPWSTR sczIgnoreDependencies; | ||
| 220 | LPWSTR sczAncestors; | ||
| 221 | |||
| 222 | BOOL fPseudoBundle; | ||
| 223 | |||
| 224 | BOOL fRepairable; | ||
| 225 | BURN_EXE_PROTOCOL_TYPE protocol; | ||
| 226 | |||
| 227 | BOOL fSupportsAncestors; | ||
| 228 | |||
| 229 | BURN_EXE_EXIT_CODE* rgExitCodes; | ||
| 230 | DWORD cExitCodes; | ||
| 231 | |||
| 232 | BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments; | ||
| 233 | DWORD cCommandLineArguments; | ||
| 234 | } Exe; | ||
| 235 | struct | ||
| 236 | { | ||
| 237 | LPWSTR sczProductCode; | ||
| 238 | DWORD dwLanguage; | ||
| 239 | DWORD64 qwVersion; | ||
| 240 | LPWSTR sczInstalledProductCode; | ||
| 241 | DWORD64 qwInstalledVersion; | ||
| 242 | BOOL fDisplayInternalUI; | ||
| 243 | LPWSTR sczUpgradeCode; | ||
| 244 | |||
| 245 | BURN_MSIPROPERTY* rgProperties; | ||
| 246 | DWORD cProperties; | ||
| 247 | |||
| 248 | BURN_MSIFEATURE* rgFeatures; | ||
| 249 | DWORD cFeatures; | ||
| 250 | |||
| 251 | BURN_RELATED_MSI* rgRelatedMsis; | ||
| 252 | DWORD cRelatedMsis; | ||
| 253 | |||
| 254 | _BURN_PACKAGE** rgpSlipstreamMspPackages; | ||
| 255 | LPWSTR* rgsczSlipstreamMspPackageIds; | ||
| 256 | DWORD cSlipstreamMspPackages; | ||
| 257 | |||
| 258 | BOOL fCompatibleInstalled; | ||
| 259 | } Msi; | ||
| 260 | struct | ||
| 261 | { | ||
| 262 | LPWSTR sczPatchCode; | ||
| 263 | LPWSTR sczApplicabilityXml; | ||
| 264 | BOOL fDisplayInternalUI; | ||
| 265 | |||
| 266 | BURN_MSIPROPERTY* rgProperties; | ||
| 267 | DWORD cProperties; | ||
| 268 | |||
| 269 | BURN_MSPTARGETPRODUCT* rgTargetProducts; | ||
| 270 | DWORD cTargetProductCodes; | ||
| 271 | } Msp; | ||
| 272 | struct | ||
| 273 | { | ||
| 274 | LPWSTR sczDetectCondition; | ||
| 275 | LPWSTR sczKB; | ||
| 276 | } Msu; | ||
| 277 | }; | ||
| 278 | } BURN_PACKAGE; | ||
| 279 | |||
| 280 | typedef struct _BURN_PACKAGES | ||
| 281 | { | ||
| 282 | BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries; | ||
| 283 | DWORD cRollbackBoundaries; | ||
| 284 | |||
| 285 | BURN_PACKAGE* rgPackages; | ||
| 286 | DWORD cPackages; | ||
| 287 | |||
| 288 | BURN_PACKAGE* rgCompatiblePackages; | ||
| 289 | DWORD cCompatiblePackages; | ||
| 290 | |||
| 291 | BURN_PATCH_TARGETCODE* rgPatchTargetCodes; | ||
| 292 | DWORD cPatchTargetCodes; | ||
| 293 | |||
| 294 | MSIPATCHSEQUENCEINFOW* rgPatchInfo; | ||
| 295 | BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes. | ||
| 296 | // Thus this array is the exact same size as rgPatchInfo. | ||
| 297 | DWORD cPatchInfo; | ||
| 298 | } BURN_PACKAGES; | ||
| 299 | |||
| 300 | |||
| 301 | // function declarations | ||
| 302 | |||
| 303 | HRESULT PackagesParseFromXml( | ||
| 304 | __in BURN_PACKAGES* pPackages, | ||
| 305 | __in BURN_PAYLOADS* pPayloads, | ||
| 306 | __in IXMLDOMNode* pixnBundle | ||
| 307 | ); | ||
| 308 | void PackageUninitialize( | ||
| 309 | __in BURN_PACKAGE* pPackage | ||
| 310 | ); | ||
| 311 | void PackagesUninitialize( | ||
| 312 | __in BURN_PACKAGES* pPackages | ||
| 313 | ); | ||
| 314 | HRESULT PackageFindById( | ||
| 315 | __in BURN_PACKAGES* pPackages, | ||
| 316 | __in_z LPCWSTR wzId, | ||
| 317 | __out BURN_PACKAGE** ppPackage | ||
| 318 | ); | ||
| 319 | HRESULT PackageFindRelatedById( | ||
| 320 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
| 321 | __in_z LPCWSTR wzId, | ||
| 322 | __out BURN_PACKAGE** ppPackage | ||
| 323 | ); | ||
| 324 | HRESULT PackageGetProperty( | ||
| 325 | __in const BURN_PACKAGE* pPackage, | ||
| 326 | __in_z LPCWSTR wzProperty, | ||
| 327 | __out_z_opt LPWSTR* psczValue | ||
| 328 | ); | ||
| 329 | HRESULT PackageEnsureCompatiblePackagesArray( | ||
| 330 | __in BURN_PACKAGES* pPackages | ||
| 331 | ); | ||
| 332 | |||
| 333 | |||
| 334 | #if defined(__cplusplus) | ||
| 335 | } | ||
| 336 | #endif | ||
diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp new file mode 100644 index 00000000..6833288f --- /dev/null +++ b/src/engine/payload.cpp | |||
| @@ -0,0 +1,367 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | static HRESULT FindEmbeddedBySourcePath( | ||
| 9 | __in BURN_PAYLOADS* pPayloads, | ||
| 10 | __in_opt BURN_CONTAINER* pContainer, | ||
| 11 | __in_z LPCWSTR wzStreamName, | ||
| 12 | __out BURN_PAYLOAD** ppPayload | ||
| 13 | ); | ||
| 14 | |||
| 15 | |||
| 16 | // function definitions | ||
| 17 | |||
| 18 | extern "C" HRESULT PayloadsParseFromXml( | ||
| 19 | __in BURN_PAYLOADS* pPayloads, | ||
| 20 | __in_opt BURN_CONTAINERS* pContainers, | ||
| 21 | __in_opt BURN_CATALOGS* pCatalogs, | ||
| 22 | __in IXMLDOMNode* pixnBundle | ||
| 23 | ) | ||
| 24 | { | ||
| 25 | HRESULT hr = S_OK; | ||
| 26 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 27 | IXMLDOMNode* pixnNode = NULL; | ||
| 28 | DWORD cNodes = 0; | ||
| 29 | LPWSTR scz = NULL; | ||
| 30 | |||
| 31 | // select payload nodes | ||
| 32 | hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes); | ||
| 33 | ExitOnFailure(hr, "Failed to select payload nodes."); | ||
| 34 | |||
| 35 | // get payload node count | ||
| 36 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 37 | ExitOnFailure(hr, "Failed to get payload node count."); | ||
| 38 | |||
| 39 | if (!cNodes) | ||
| 40 | { | ||
| 41 | ExitFunction(); | ||
| 42 | } | ||
| 43 | |||
| 44 | // allocate memory for payloads | ||
| 45 | pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE); | ||
| 46 | ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs."); | ||
| 47 | |||
| 48 | pPayloads->cPayloads = cNodes; | ||
| 49 | |||
| 50 | // parse search elements | ||
| 51 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 52 | { | ||
| 53 | BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; | ||
| 54 | |||
| 55 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 56 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 57 | |||
| 58 | // @Id | ||
| 59 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey); | ||
| 60 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 61 | |||
| 62 | // @FilePath | ||
| 63 | hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath); | ||
| 64 | ExitOnFailure(hr, "Failed to get @FilePath."); | ||
| 65 | |||
| 66 | // @Packaging | ||
| 67 | hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz); | ||
| 68 | ExitOnFailure(hr, "Failed to get @Packaging."); | ||
| 69 | |||
| 70 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"download", -1)) | ||
| 71 | { | ||
| 72 | pPayload->packaging = BURN_PAYLOAD_PACKAGING_DOWNLOAD; | ||
| 73 | } | ||
| 74 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1)) | ||
| 75 | { | ||
| 76 | pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED; | ||
| 77 | } | ||
| 78 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1)) | ||
| 79 | { | ||
| 80 | pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; | ||
| 81 | } | ||
| 82 | else | ||
| 83 | { | ||
| 84 | hr = E_INVALIDARG; | ||
| 85 | ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz); | ||
| 86 | } | ||
| 87 | |||
| 88 | // @Container | ||
| 89 | if (pContainers) | ||
| 90 | { | ||
| 91 | hr = XmlGetAttributeEx(pixnNode, L"Container", &scz); | ||
| 92 | if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) | ||
| 93 | { | ||
| 94 | ExitOnFailure(hr, "Failed to get @Container."); | ||
| 95 | |||
| 96 | // find container | ||
| 97 | hr = ContainerFindById(pContainers, scz, &pPayload->pContainer); | ||
| 98 | ExitOnFailure(hr, "Failed to to find container: %ls", scz); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | // @LayoutOnly | ||
| 103 | hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly); | ||
| 104 | if (E_NOTFOUND != hr) | ||
| 105 | { | ||
| 106 | ExitOnFailure(hr, "Failed to get @LayoutOnly."); | ||
| 107 | } | ||
| 108 | |||
| 109 | // @SourcePath | ||
| 110 | hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath); | ||
| 111 | if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD != pPayload->packaging) | ||
| 112 | { | ||
| 113 | ExitOnFailure(hr, "Failed to get @SourcePath."); | ||
| 114 | } | ||
| 115 | |||
| 116 | // @DownloadUrl | ||
| 117 | hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl); | ||
| 118 | if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD == pPayload->packaging) | ||
| 119 | { | ||
| 120 | ExitOnFailure(hr, "Failed to get @DownloadUrl."); | ||
| 121 | } | ||
| 122 | |||
| 123 | // @FileSize | ||
| 124 | hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz); | ||
| 125 | if (E_NOTFOUND != hr) | ||
| 126 | { | ||
| 127 | ExitOnFailure(hr, "Failed to get @FileSize."); | ||
| 128 | |||
| 129 | hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize); | ||
| 130 | ExitOnFailure(hr, "Failed to parse @FileSize."); | ||
| 131 | } | ||
| 132 | |||
| 133 | // @CertificateAuthorityKeyIdentifier | ||
| 134 | hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz); | ||
| 135 | if (E_NOTFOUND != hr) | ||
| 136 | { | ||
| 137 | ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier."); | ||
| 138 | |||
| 139 | hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier); | ||
| 140 | ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier."); | ||
| 141 | } | ||
| 142 | |||
| 143 | // @CertificateThumbprint | ||
| 144 | hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz); | ||
| 145 | if (E_NOTFOUND != hr) | ||
| 146 | { | ||
| 147 | ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint."); | ||
| 148 | |||
| 149 | hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint); | ||
| 150 | ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint."); | ||
| 151 | } | ||
| 152 | |||
| 153 | // @Hash | ||
| 154 | hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); | ||
| 155 | ExitOnFailure(hr, "Failed to get @Hash."); | ||
| 156 | |||
| 157 | hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash); | ||
| 158 | ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash."); | ||
| 159 | |||
| 160 | // @Catalog | ||
| 161 | hr = XmlGetAttributeEx(pixnNode, L"Catalog", &scz); | ||
| 162 | if (E_NOTFOUND != hr) | ||
| 163 | { | ||
| 164 | ExitOnFailure(hr, "Failed to get @Catalog."); | ||
| 165 | |||
| 166 | hr = CatalogFindById(pCatalogs, scz, &pPayload->pCatalog); | ||
| 167 | ExitOnFailure(hr, "Failed to find catalog."); | ||
| 168 | } | ||
| 169 | |||
| 170 | // prepare next iteration | ||
| 171 | ReleaseNullObject(pixnNode); | ||
| 172 | } | ||
| 173 | |||
| 174 | hr = S_OK; | ||
| 175 | |||
| 176 | LExit: | ||
| 177 | ReleaseObject(pixnNodes); | ||
| 178 | ReleaseObject(pixnNode); | ||
| 179 | ReleaseStr(scz); | ||
| 180 | |||
| 181 | return hr; | ||
| 182 | } | ||
| 183 | |||
| 184 | extern "C" void PayloadsUninitialize( | ||
| 185 | __in BURN_PAYLOADS* pPayloads | ||
| 186 | ) | ||
| 187 | { | ||
| 188 | if (pPayloads->rgPayloads) | ||
| 189 | { | ||
| 190 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 191 | { | ||
| 192 | BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; | ||
| 193 | |||
| 194 | ReleaseStr(pPayload->sczKey); | ||
| 195 | ReleaseStr(pPayload->sczFilePath); | ||
| 196 | ReleaseMem(pPayload->pbHash); | ||
| 197 | ReleaseMem(pPayload->pbCertificateRootThumbprint); | ||
| 198 | ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier); | ||
| 199 | ReleaseStr(pPayload->sczSourcePath); | ||
| 200 | ReleaseStr(pPayload->sczLocalFilePath); | ||
| 201 | ReleaseStr(pPayload->downloadSource.sczUrl); | ||
| 202 | ReleaseStr(pPayload->downloadSource.sczUser); | ||
| 203 | ReleaseStr(pPayload->downloadSource.sczPassword); | ||
| 204 | } | ||
| 205 | MemFree(pPayloads->rgPayloads); | ||
| 206 | } | ||
| 207 | |||
| 208 | // clear struct | ||
| 209 | memset(pPayloads, 0, sizeof(BURN_PAYLOADS)); | ||
| 210 | } | ||
| 211 | |||
| 212 | extern "C" HRESULT PayloadExtractFromContainer( | ||
| 213 | __in BURN_PAYLOADS* pPayloads, | ||
| 214 | __in_opt BURN_CONTAINER* pContainer, | ||
| 215 | __in BURN_CONTAINER_CONTEXT* pContainerContext, | ||
| 216 | __in_z LPCWSTR wzTargetDir | ||
| 217 | ) | ||
| 218 | { | ||
| 219 | HRESULT hr = S_OK; | ||
| 220 | LPWSTR sczStreamName = NULL; | ||
| 221 | LPWSTR sczDirectory = NULL; | ||
| 222 | BURN_PAYLOAD* pPayload = NULL; | ||
| 223 | |||
| 224 | // extract all payloads | ||
| 225 | for (;;) | ||
| 226 | { | ||
| 227 | // get next stream | ||
| 228 | hr = ContainerNextStream(pContainerContext, &sczStreamName); | ||
| 229 | if (E_NOMOREITEMS == hr) | ||
| 230 | { | ||
| 231 | hr = S_OK; | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | ExitOnFailure(hr, "Failed to get next stream."); | ||
| 235 | |||
| 236 | // find payload by stream name | ||
| 237 | hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload); | ||
| 238 | ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName); | ||
| 239 | |||
| 240 | // make file path | ||
| 241 | hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath); | ||
| 242 | ExitOnFailure(hr, "Failed to concat file paths."); | ||
| 243 | |||
| 244 | // extract file | ||
| 245 | hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory); | ||
| 246 | ExitOnFailure(hr, "Failed to get directory portion of local file path"); | ||
| 247 | |||
| 248 | hr = DirEnsureExists(sczDirectory, NULL); | ||
| 249 | ExitOnFailure(hr, "Failed to ensure directory exists"); | ||
| 250 | |||
| 251 | hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath); | ||
| 252 | ExitOnFailure(hr, "Failed to extract file."); | ||
| 253 | |||
| 254 | // flag that the payload has been acquired | ||
| 255 | pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED; | ||
| 256 | } | ||
| 257 | |||
| 258 | // locate any payloads that were not extracted | ||
| 259 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 260 | { | ||
| 261 | pPayload = &pPayloads->rgPayloads[i]; | ||
| 262 | |||
| 263 | // if the payload is part of the container | ||
| 264 | if (!pContainer || pPayload->pContainer == pContainer) | ||
| 265 | { | ||
| 266 | // if the payload has not been acquired | ||
| 267 | if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state) | ||
| 268 | { | ||
| 269 | hr = E_INVALIDDATA; | ||
| 270 | ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | LExit: | ||
| 276 | ReleaseStr(sczStreamName); | ||
| 277 | ReleaseStr(sczDirectory); | ||
| 278 | |||
| 279 | return hr; | ||
| 280 | } | ||
| 281 | |||
| 282 | extern "C" HRESULT PayloadFindById( | ||
| 283 | __in BURN_PAYLOADS* pPayloads, | ||
| 284 | __in_z LPCWSTR wzId, | ||
| 285 | __out BURN_PAYLOAD** ppPayload | ||
| 286 | ) | ||
| 287 | { | ||
| 288 | HRESULT hr = S_OK; | ||
| 289 | BURN_PAYLOAD* pPayload = NULL; | ||
| 290 | |||
| 291 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 292 | { | ||
| 293 | pPayload = &pPayloads->rgPayloads[i]; | ||
| 294 | |||
| 295 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1)) | ||
| 296 | { | ||
| 297 | *ppPayload = pPayload; | ||
| 298 | ExitFunction1(hr = S_OK); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | hr = E_NOTFOUND; | ||
| 303 | |||
| 304 | LExit: | ||
| 305 | return hr; | ||
| 306 | } | ||
| 307 | |||
| 308 | extern "C" HRESULT PayloadFindEmbeddedBySourcePath( | ||
| 309 | __in BURN_PAYLOADS* pPayloads, | ||
| 310 | __in_z LPCWSTR wzStreamName, | ||
| 311 | __out BURN_PAYLOAD** ppPayload | ||
| 312 | ) | ||
| 313 | { | ||
| 314 | HRESULT hr = S_OK; | ||
| 315 | BURN_PAYLOAD* pPayload = NULL; | ||
| 316 | |||
| 317 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 318 | { | ||
| 319 | pPayload = &pPayloads->rgPayloads[i]; | ||
| 320 | |||
| 321 | if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging) | ||
| 322 | { | ||
| 323 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) | ||
| 324 | { | ||
| 325 | *ppPayload = pPayload; | ||
| 326 | ExitFunction1(hr = S_OK); | ||
| 327 | } | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | hr = E_NOTFOUND; | ||
| 332 | |||
| 333 | LExit: | ||
| 334 | return hr; | ||
| 335 | } | ||
| 336 | |||
| 337 | |||
| 338 | // internal function definitions | ||
| 339 | |||
| 340 | static HRESULT FindEmbeddedBySourcePath( | ||
| 341 | __in BURN_PAYLOADS* pPayloads, | ||
| 342 | __in_opt BURN_CONTAINER* pContainer, | ||
| 343 | __in_z LPCWSTR wzStreamName, | ||
| 344 | __out BURN_PAYLOAD** ppPayload | ||
| 345 | ) | ||
| 346 | { | ||
| 347 | HRESULT hr = S_OK; | ||
| 348 | |||
| 349 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 350 | { | ||
| 351 | BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i]; | ||
| 352 | |||
| 353 | if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging && (!pContainer || pPayload->pContainer == pContainer)) | ||
| 354 | { | ||
| 355 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1)) | ||
| 356 | { | ||
| 357 | *ppPayload = pPayload; | ||
| 358 | ExitFunction1(hr = S_OK); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | hr = E_NOTFOUND; | ||
| 364 | |||
| 365 | LExit: | ||
| 366 | return hr; | ||
| 367 | } | ||
diff --git a/src/engine/payload.h b/src/engine/payload.h new file mode 100644 index 00000000..0c7b68e4 --- /dev/null +++ b/src/engine/payload.h | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | enum BURN_PAYLOAD_PACKAGING | ||
| 13 | { | ||
| 14 | BURN_PAYLOAD_PACKAGING_NONE, | ||
| 15 | BURN_PAYLOAD_PACKAGING_DOWNLOAD, | ||
| 16 | BURN_PAYLOAD_PACKAGING_EMBEDDED, | ||
| 17 | BURN_PAYLOAD_PACKAGING_EXTERNAL, | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum BURN_PAYLOAD_STATE | ||
| 21 | { | ||
| 22 | BURN_PAYLOAD_STATE_NONE, | ||
| 23 | BURN_PAYLOAD_STATE_ACQUIRED, | ||
| 24 | BURN_PAYLOAD_STATE_CACHED, | ||
| 25 | }; | ||
| 26 | |||
| 27 | |||
| 28 | // structs | ||
| 29 | |||
| 30 | typedef struct _BURN_PAYLOAD | ||
| 31 | { | ||
| 32 | LPWSTR sczKey; | ||
| 33 | BURN_PAYLOAD_PACKAGING packaging; | ||
| 34 | BOOL fLayoutOnly; | ||
| 35 | DWORD64 qwFileSize; | ||
| 36 | LPWSTR sczFilePath; // file path relative to the execute location | ||
| 37 | |||
| 38 | BURN_CATALOG *pCatalog; // used to verify this payload | ||
| 39 | BYTE* pbCertificateRootPublicKeyIdentifier; | ||
| 40 | DWORD cbCertificateRootPublicKeyIdentifier; | ||
| 41 | BYTE* pbCertificateRootThumbprint; | ||
| 42 | DWORD cbCertificateRootThumbprint; | ||
| 43 | BYTE* pbHash; | ||
| 44 | DWORD cbHash; | ||
| 45 | |||
| 46 | LPWSTR sczSourcePath; | ||
| 47 | BURN_CONTAINER* pContainer; | ||
| 48 | DOWNLOAD_SOURCE downloadSource; | ||
| 49 | |||
| 50 | // mutable members | ||
| 51 | BURN_PAYLOAD_STATE state; | ||
| 52 | LPWSTR sczLocalFilePath; // location of extracted or downloaded copy | ||
| 53 | } BURN_PAYLOAD; | ||
| 54 | |||
| 55 | typedef struct _BURN_PAYLOADS | ||
| 56 | { | ||
| 57 | BURN_PAYLOAD* rgPayloads; | ||
| 58 | DWORD cPayloads; | ||
| 59 | } BURN_PAYLOADS; | ||
| 60 | |||
| 61 | |||
| 62 | // functions | ||
| 63 | |||
| 64 | HRESULT PayloadsParseFromXml( | ||
| 65 | __in BURN_PAYLOADS* pPayloads, | ||
| 66 | __in_opt BURN_CONTAINERS* pContainers, | ||
| 67 | __in_opt BURN_CATALOGS* pCatalogs, | ||
| 68 | __in IXMLDOMNode* pixnBundle | ||
| 69 | ); | ||
| 70 | void PayloadsUninitialize( | ||
| 71 | __in BURN_PAYLOADS* pPayloads | ||
| 72 | ); | ||
| 73 | HRESULT PayloadExtractFromContainer( | ||
| 74 | __in BURN_PAYLOADS* pPayloads, | ||
| 75 | __in_opt BURN_CONTAINER* pContainer, | ||
| 76 | __in BURN_CONTAINER_CONTEXT* pContainerContext, | ||
| 77 | __in_z LPCWSTR wzTargetDir | ||
| 78 | ); | ||
| 79 | HRESULT PayloadFindById( | ||
| 80 | __in BURN_PAYLOADS* pPayloads, | ||
| 81 | __in_z LPCWSTR wzId, | ||
| 82 | __out BURN_PAYLOAD** ppPayload | ||
| 83 | ); | ||
| 84 | HRESULT PayloadFindEmbeddedBySourcePath( | ||
| 85 | __in BURN_PAYLOADS* pPayloads, | ||
| 86 | __in_z LPCWSTR wzStreamName, | ||
| 87 | __out BURN_PAYLOAD** ppPayload | ||
| 88 | ); | ||
| 89 | |||
| 90 | |||
| 91 | #if defined(__cplusplus) | ||
| 92 | } | ||
| 93 | #endif | ||
diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp new file mode 100644 index 00000000..7ecc4859 --- /dev/null +++ b/src/engine/pipe.cpp | |||
| @@ -0,0 +1,873 @@ | |||
| 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 | |||
| 5 | static const DWORD PIPE_64KB = 64 * 1024; | ||
| 6 | static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, | ||
| 7 | static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. | ||
| 8 | |||
| 9 | static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; | ||
| 10 | static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; | ||
| 11 | |||
| 12 | static HRESULT AllocatePipeMessage( | ||
| 13 | __in DWORD dwMessage, | ||
| 14 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 15 | __in DWORD cbData, | ||
| 16 | __out_bcount(cb) LPVOID* ppvMessage, | ||
| 17 | __out DWORD* cbMessage | ||
| 18 | ); | ||
| 19 | static void FreePipeMessage( | ||
| 20 | __in BURN_PIPE_MESSAGE *pMsg | ||
| 21 | ); | ||
| 22 | static HRESULT WritePipeMessage( | ||
| 23 | __in HANDLE hPipe, | ||
| 24 | __in DWORD dwMessage, | ||
| 25 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 26 | __in DWORD cbData | ||
| 27 | ); | ||
| 28 | static HRESULT GetPipeMessage( | ||
| 29 | __in HANDLE hPipe, | ||
| 30 | __in BURN_PIPE_MESSAGE* pMsg | ||
| 31 | ); | ||
| 32 | static HRESULT ChildPipeConnected( | ||
| 33 | __in HANDLE hPipe, | ||
| 34 | __in_z LPCWSTR wzSecret, | ||
| 35 | __inout DWORD* pdwProcessId | ||
| 36 | ); | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | /******************************************************************* | ||
| 41 | PipeConnectionInitialize - initialize pipe connection data. | ||
| 42 | |||
| 43 | *******************************************************************/ | ||
| 44 | void PipeConnectionInitialize( | ||
| 45 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 46 | ) | ||
| 47 | { | ||
| 48 | memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); | ||
| 49 | pConnection->hPipe = INVALID_HANDLE_VALUE; | ||
| 50 | pConnection->hCachePipe = INVALID_HANDLE_VALUE; | ||
| 51 | } | ||
| 52 | |||
| 53 | /******************************************************************* | ||
| 54 | PipeConnectionUninitialize - free data in a pipe connection. | ||
| 55 | |||
| 56 | *******************************************************************/ | ||
| 57 | void PipeConnectionUninitialize( | ||
| 58 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 59 | ) | ||
| 60 | { | ||
| 61 | ReleaseFileHandle(pConnection->hCachePipe); | ||
| 62 | ReleaseFileHandle(pConnection->hPipe); | ||
| 63 | ReleaseHandle(pConnection->hProcess); | ||
| 64 | ReleaseStr(pConnection->sczSecret); | ||
| 65 | ReleaseStr(pConnection->sczName); | ||
| 66 | |||
| 67 | memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); | ||
| 68 | pConnection->hPipe = INVALID_HANDLE_VALUE; | ||
| 69 | pConnection->hCachePipe = INVALID_HANDLE_VALUE; | ||
| 70 | } | ||
| 71 | |||
| 72 | /******************************************************************* | ||
| 73 | PipeSendMessage - | ||
| 74 | |||
| 75 | *******************************************************************/ | ||
| 76 | extern "C" HRESULT PipeSendMessage( | ||
| 77 | __in HANDLE hPipe, | ||
| 78 | __in DWORD dwMessage, | ||
| 79 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 80 | __in DWORD cbData, | ||
| 81 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, | ||
| 82 | __in_opt LPVOID pvContext, | ||
| 83 | __out DWORD* pdwResult | ||
| 84 | ) | ||
| 85 | { | ||
| 86 | HRESULT hr = S_OK; | ||
| 87 | BURN_PIPE_RESULT result = { }; | ||
| 88 | |||
| 89 | hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); | ||
| 90 | ExitOnFailure(hr, "Failed to write send message to pipe."); | ||
| 91 | |||
| 92 | hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); | ||
| 93 | ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); | ||
| 94 | |||
| 95 | *pdwResult = result.dwResult; | ||
| 96 | |||
| 97 | LExit: | ||
| 98 | return hr; | ||
| 99 | } | ||
| 100 | |||
| 101 | /******************************************************************* | ||
| 102 | PipePumpMessages - | ||
| 103 | |||
| 104 | *******************************************************************/ | ||
| 105 | extern "C" HRESULT PipePumpMessages( | ||
| 106 | __in HANDLE hPipe, | ||
| 107 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, | ||
| 108 | __in_opt LPVOID pvContext, | ||
| 109 | __in BURN_PIPE_RESULT* pResult | ||
| 110 | ) | ||
| 111 | { | ||
| 112 | HRESULT hr = S_OK; | ||
| 113 | BURN_PIPE_MESSAGE msg = { }; | ||
| 114 | SIZE_T iData = 0; | ||
| 115 | LPSTR sczMessage = NULL; | ||
| 116 | DWORD dwResult = 0; | ||
| 117 | |||
| 118 | // Pump messages from child process. | ||
| 119 | while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) | ||
| 120 | { | ||
| 121 | switch (msg.dwMessage) | ||
| 122 | { | ||
| 123 | case BURN_PIPE_MESSAGE_TYPE_LOG: | ||
| 124 | iData = 0; | ||
| 125 | |||
| 126 | hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); | ||
| 127 | ExitOnFailure(hr, "Failed to read log message."); | ||
| 128 | |||
| 129 | hr = LogStringWorkRaw(sczMessage); | ||
| 130 | ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); | ||
| 131 | |||
| 132 | dwResult = static_cast<DWORD>(hr); | ||
| 133 | break; | ||
| 134 | |||
| 135 | case BURN_PIPE_MESSAGE_TYPE_COMPLETE: | ||
| 136 | if (!msg.pvData || sizeof(DWORD) != msg.cbData) | ||
| 137 | { | ||
| 138 | hr = E_INVALIDARG; | ||
| 139 | ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); | ||
| 140 | } | ||
| 141 | |||
| 142 | pResult->dwResult = *static_cast<DWORD*>(msg.pvData); | ||
| 143 | ExitFunction1(hr = S_OK); // exit loop. | ||
| 144 | |||
| 145 | case BURN_PIPE_MESSAGE_TYPE_TERMINATE: | ||
| 146 | iData = 0; | ||
| 147 | |||
| 148 | hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult); | ||
| 149 | ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); | ||
| 150 | |||
| 151 | if (sizeof(DWORD) * 2 == msg.cbData) | ||
| 152 | { | ||
| 153 | hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); | ||
| 154 | ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); | ||
| 155 | } | ||
| 156 | |||
| 157 | ExitFunction1(hr = S_OK); // exit loop. | ||
| 158 | |||
| 159 | default: | ||
| 160 | if (pfnCallback) | ||
| 161 | { | ||
| 162 | hr = pfnCallback(&msg, pvContext, &dwResult); | ||
| 163 | } | ||
| 164 | else | ||
| 165 | { | ||
| 166 | hr = E_INVALIDARG; | ||
| 167 | } | ||
| 168 | ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | |||
| 172 | // post result | ||
| 173 | hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); | ||
| 174 | ExitOnFailure(hr, "Failed to post result to child process."); | ||
| 175 | |||
| 176 | FreePipeMessage(&msg); | ||
| 177 | } | ||
| 178 | ExitOnFailure(hr, "Failed to get message over pipe"); | ||
| 179 | |||
| 180 | if (S_FALSE == hr) | ||
| 181 | { | ||
| 182 | hr = S_OK; | ||
| 183 | } | ||
| 184 | |||
| 185 | LExit: | ||
| 186 | ReleaseStr(sczMessage); | ||
| 187 | FreePipeMessage(&msg); | ||
| 188 | |||
| 189 | return hr; | ||
| 190 | } | ||
| 191 | |||
| 192 | /******************************************************************* | ||
| 193 | PipeCreateNameAndSecret - | ||
| 194 | |||
| 195 | *******************************************************************/ | ||
| 196 | extern "C" HRESULT PipeCreateNameAndSecret( | ||
| 197 | __out_z LPWSTR *psczConnectionName, | ||
| 198 | __out_z LPWSTR *psczSecret | ||
| 199 | ) | ||
| 200 | { | ||
| 201 | HRESULT hr = S_OK; | ||
| 202 | WCHAR wzGuid[GUID_STRING_LENGTH]; | ||
| 203 | LPWSTR sczConnectionName = NULL; | ||
| 204 | LPWSTR sczSecret = NULL; | ||
| 205 | |||
| 206 | // Create the unique pipe name. | ||
| 207 | hr = GuidFixedCreate(wzGuid); | ||
| 208 | ExitOnRootFailure(hr, "Failed to create pipe guid."); | ||
| 209 | |||
| 210 | hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); | ||
| 211 | ExitOnFailure(hr, "Failed to allocate pipe name."); | ||
| 212 | |||
| 213 | // Create the unique client secret. | ||
| 214 | hr = GuidFixedCreate(wzGuid); | ||
| 215 | ExitOnRootFailure(hr, "Failed to create pipe secret."); | ||
| 216 | |||
| 217 | hr = StrAllocString(&sczSecret, wzGuid, 0); | ||
| 218 | ExitOnFailure(hr, "Failed to allocate pipe secret."); | ||
| 219 | |||
| 220 | *psczConnectionName = sczConnectionName; | ||
| 221 | sczConnectionName = NULL; | ||
| 222 | *psczSecret = sczSecret; | ||
| 223 | sczSecret = NULL; | ||
| 224 | |||
| 225 | LExit: | ||
| 226 | ReleaseStr(sczSecret); | ||
| 227 | ReleaseStr(sczConnectionName); | ||
| 228 | |||
| 229 | return hr; | ||
| 230 | } | ||
| 231 | |||
| 232 | /******************************************************************* | ||
| 233 | PipeCreatePipes - create the pipes and event to signal child process. | ||
| 234 | |||
| 235 | *******************************************************************/ | ||
| 236 | extern "C" HRESULT PipeCreatePipes( | ||
| 237 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 238 | __in BOOL fCreateCachePipe, | ||
| 239 | __out HANDLE* phEvent | ||
| 240 | ) | ||
| 241 | { | ||
| 242 | Assert(pConnection->sczName); | ||
| 243 | Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); | ||
| 244 | Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); | ||
| 245 | |||
| 246 | HRESULT hr = S_OK; | ||
| 247 | PSECURITY_DESCRIPTOR psd = NULL; | ||
| 248 | SECURITY_ATTRIBUTES sa = { }; | ||
| 249 | LPWSTR sczFullPipeName = NULL; | ||
| 250 | HANDLE hPipe = INVALID_HANDLE_VALUE; | ||
| 251 | HANDLE hCachePipe = INVALID_HANDLE_VALUE; | ||
| 252 | |||
| 253 | // Only the grant special rights when the pipe is being used for "embedded" | ||
| 254 | // scenarios (aka: there is no cache pipe). | ||
| 255 | if (!fCreateCachePipe) | ||
| 256 | { | ||
| 257 | // Create the security descriptor that grants read/write/sync access to Everyone. | ||
| 258 | // TODO: consider locking down "WD" to LogonIds (logon session) | ||
| 259 | LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; | ||
| 260 | if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) | ||
| 261 | { | ||
| 262 | ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); | ||
| 263 | } | ||
| 264 | |||
| 265 | sa.nLength = sizeof(sa); | ||
| 266 | sa.lpSecurityDescriptor = psd; | ||
| 267 | sa.bInheritHandle = FALSE; | ||
| 268 | } | ||
| 269 | |||
| 270 | // Create the pipe. | ||
| 271 | hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); | ||
| 272 | ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); | ||
| 273 | |||
| 274 | // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. | ||
| 275 | 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); | ||
| 276 | if (INVALID_HANDLE_VALUE == hPipe) | ||
| 277 | { | ||
| 278 | ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); | ||
| 279 | } | ||
| 280 | |||
| 281 | if (fCreateCachePipe) | ||
| 282 | { | ||
| 283 | // Create the cache pipe. | ||
| 284 | hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); | ||
| 285 | ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); | ||
| 286 | |||
| 287 | 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); | ||
| 288 | if (INVALID_HANDLE_VALUE == hCachePipe) | ||
| 289 | { | ||
| 290 | ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | pConnection->hCachePipe = hCachePipe; | ||
| 295 | hCachePipe = INVALID_HANDLE_VALUE; | ||
| 296 | |||
| 297 | pConnection->hPipe = hPipe; | ||
| 298 | hPipe = INVALID_HANDLE_VALUE; | ||
| 299 | |||
| 300 | // TODO: remove the following | ||
| 301 | *phEvent = NULL; | ||
| 302 | |||
| 303 | LExit: | ||
| 304 | ReleaseFileHandle(hCachePipe); | ||
| 305 | ReleaseFileHandle(hPipe); | ||
| 306 | ReleaseStr(sczFullPipeName); | ||
| 307 | |||
| 308 | if (psd) | ||
| 309 | { | ||
| 310 | ::LocalFree(psd); | ||
| 311 | } | ||
| 312 | |||
| 313 | return hr; | ||
| 314 | } | ||
| 315 | |||
| 316 | /******************************************************************* | ||
| 317 | PipeLaunchParentProcess - Called from the per-machine process to create | ||
| 318 | a per-user process and set up the | ||
| 319 | communication pipe. | ||
| 320 | |||
| 321 | *******************************************************************/ | ||
| 322 | const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; | ||
| 323 | HRESULT PipeLaunchParentProcess( | ||
| 324 | __in_z LPCWSTR wzCommandLine, | ||
| 325 | __in int nCmdShow, | ||
| 326 | __in_z LPWSTR sczConnectionName, | ||
| 327 | __in_z LPWSTR sczSecret, | ||
| 328 | __in BOOL /*fDisableUnelevate*/ | ||
| 329 | ) | ||
| 330 | { | ||
| 331 | HRESULT hr = S_OK; | ||
| 332 | DWORD dwProcessId = 0; | ||
| 333 | LPWSTR sczBurnPath = NULL; | ||
| 334 | LPWSTR sczParameters = NULL; | ||
| 335 | HANDLE hProcess = NULL; | ||
| 336 | |||
| 337 | dwProcessId = ::GetCurrentProcessId(); | ||
| 338 | |||
| 339 | hr = PathForCurrentProcess(&sczBurnPath, NULL); | ||
| 340 | ExitOnFailure(hr, "Failed to get current process path."); | ||
| 341 | |||
| 342 | hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine); | ||
| 343 | ExitOnFailure(hr, "Failed to allocate parameters for unelevated process."); | ||
| 344 | |||
| 345 | #ifdef ENABLE_UNELEVATE | ||
| 346 | if (fDisableUnelevate) | ||
| 347 | { | ||
| 348 | hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); | ||
| 349 | ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); | ||
| 350 | } | ||
| 351 | else | ||
| 352 | { | ||
| 353 | // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated). | ||
| 354 | hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess); | ||
| 355 | if (FAILED(hr)) | ||
| 356 | { | ||
| 357 | hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow); | ||
| 358 | if (FAILED(hr)) | ||
| 359 | { | ||
| 360 | hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL); | ||
| 361 | ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | } | ||
| 365 | #else | ||
| 366 | hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess); | ||
| 367 | ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath); | ||
| 368 | #endif | ||
| 369 | |||
| 370 | LExit: | ||
| 371 | ReleaseHandle(hProcess); | ||
| 372 | ReleaseStr(sczParameters); | ||
| 373 | ReleaseStr(sczBurnPath); | ||
| 374 | |||
| 375 | return hr; | ||
| 376 | } | ||
| 377 | |||
| 378 | /******************************************************************* | ||
| 379 | PipeLaunchChildProcess - Called from the per-user process to create | ||
| 380 | the per-machine process and set up the | ||
| 381 | communication pipe. | ||
| 382 | |||
| 383 | *******************************************************************/ | ||
| 384 | extern "C" HRESULT PipeLaunchChildProcess( | ||
| 385 | __in_z LPCWSTR wzExecutablePath, | ||
| 386 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 387 | __in BOOL fElevate, | ||
| 388 | __in_opt HWND hwndParent | ||
| 389 | ) | ||
| 390 | { | ||
| 391 | HRESULT hr = S_OK; | ||
| 392 | DWORD dwCurrentProcessId = ::GetCurrentProcessId(); | ||
| 393 | LPWSTR sczParameters = NULL; | ||
| 394 | OS_VERSION osVersion = OS_VERSION_UNKNOWN; | ||
| 395 | DWORD dwServicePack = 0; | ||
| 396 | LPCWSTR wzVerb = NULL; | ||
| 397 | HANDLE hProcess = NULL; | ||
| 398 | |||
| 399 | hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId); | ||
| 400 | ExitOnFailure(hr, "Failed to allocate parameters for elevated process."); | ||
| 401 | |||
| 402 | OsGetVersion(&osVersion, &dwServicePack); | ||
| 403 | wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas"; | ||
| 404 | |||
| 405 | // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine. | ||
| 406 | // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary. | ||
| 407 | hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess); | ||
| 408 | ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath); | ||
| 409 | |||
| 410 | pConnection->dwProcessId = ::GetProcessId(hProcess); | ||
| 411 | pConnection->hProcess = hProcess; | ||
| 412 | hProcess = NULL; | ||
| 413 | |||
| 414 | LExit: | ||
| 415 | ReleaseHandle(hProcess); | ||
| 416 | ReleaseStr(sczParameters); | ||
| 417 | |||
| 418 | return hr; | ||
| 419 | } | ||
| 420 | |||
| 421 | /******************************************************************* | ||
| 422 | PipeWaitForChildConnect - | ||
| 423 | |||
| 424 | *******************************************************************/ | ||
| 425 | extern "C" HRESULT PipeWaitForChildConnect( | ||
| 426 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 427 | ) | ||
| 428 | { | ||
| 429 | HRESULT hr = S_OK; | ||
| 430 | HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe}; | ||
| 431 | LPCWSTR wzSecret = pConnection->sczSecret; | ||
| 432 | DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); | ||
| 433 | DWORD dwCurrentProcessId = ::GetCurrentProcessId(); | ||
| 434 | DWORD dwAck = 0; | ||
| 435 | DWORD cb = 0; | ||
| 436 | |||
| 437 | for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) | ||
| 438 | { | ||
| 439 | HANDLE hPipe = hPipes[i]; | ||
| 440 | DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; | ||
| 441 | |||
| 442 | // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever | ||
| 443 | // if the child decides not to show up. | ||
| 444 | if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) | ||
| 445 | { | ||
| 446 | ExitWithLastError(hr, "Failed to set pipe to non-blocking."); | ||
| 447 | } | ||
| 448 | |||
| 449 | // Loop for a while waiting for a connection from child process. | ||
| 450 | DWORD cRetry = 0; | ||
| 451 | do | ||
| 452 | { | ||
| 453 | if (!::ConnectNamedPipe(hPipe, NULL)) | ||
| 454 | { | ||
| 455 | DWORD er = ::GetLastError(); | ||
| 456 | if (ERROR_PIPE_CONNECTED == er) | ||
| 457 | { | ||
| 458 | hr = S_OK; | ||
| 459 | break; | ||
| 460 | } | ||
| 461 | else if (ERROR_PIPE_LISTENING == er) | ||
| 462 | { | ||
| 463 | if (cRetry < PIPE_RETRY_FOR_CONNECTION) | ||
| 464 | { | ||
| 465 | hr = HRESULT_FROM_WIN32(er); | ||
| 466 | } | ||
| 467 | else | ||
| 468 | { | ||
| 469 | hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); | ||
| 470 | break; | ||
| 471 | } | ||
| 472 | |||
| 473 | ++cRetry; | ||
| 474 | ::Sleep(PIPE_WAIT_FOR_CONNECTION); | ||
| 475 | } | ||
| 476 | else | ||
| 477 | { | ||
| 478 | hr = HRESULT_FROM_WIN32(er); | ||
| 479 | break; | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); | ||
| 483 | ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); | ||
| 484 | |||
| 485 | // Put the pipe back in blocking mode. | ||
| 486 | dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; | ||
| 487 | if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) | ||
| 488 | { | ||
| 489 | ExitWithLastError(hr, "Failed to reset pipe to blocking."); | ||
| 490 | } | ||
| 491 | |||
| 492 | // Prove we are the one that created the elevated process by passing the secret. | ||
| 493 | if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL)) | ||
| 494 | { | ||
| 495 | ExitWithLastError(hr, "Failed to write secret length to pipe."); | ||
| 496 | } | ||
| 497 | |||
| 498 | if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL)) | ||
| 499 | { | ||
| 500 | ExitWithLastError(hr, "Failed to write secret to pipe."); | ||
| 501 | } | ||
| 502 | |||
| 503 | if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL)) | ||
| 504 | { | ||
| 505 | ExitWithLastError(hr, "Failed to write our process id to pipe."); | ||
| 506 | } | ||
| 507 | |||
| 508 | // Wait until the elevated process responds that it is ready to go. | ||
| 509 | if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) | ||
| 510 | { | ||
| 511 | ExitWithLastError(hr, "Failed to read ACK from pipe."); | ||
| 512 | } | ||
| 513 | |||
| 514 | // The ACK should match out expected child process id. | ||
| 515 | //if (pConnection->dwProcessId != dwAck) | ||
| 516 | //{ | ||
| 517 | // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 518 | // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); | ||
| 519 | //} | ||
| 520 | } | ||
| 521 | |||
| 522 | LExit: | ||
| 523 | return hr; | ||
| 524 | } | ||
| 525 | |||
| 526 | /******************************************************************* | ||
| 527 | PipeTerminateChildProcess - | ||
| 528 | |||
| 529 | *******************************************************************/ | ||
| 530 | extern "C" HRESULT PipeTerminateChildProcess( | ||
| 531 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 532 | __in DWORD dwParentExitCode, | ||
| 533 | __in BOOL fRestart | ||
| 534 | ) | ||
| 535 | { | ||
| 536 | HRESULT hr = S_OK; | ||
| 537 | BYTE* pbData = NULL; | ||
| 538 | SIZE_T cbData = 0; | ||
| 539 | |||
| 540 | // Prepare the exit message. | ||
| 541 | hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); | ||
| 542 | ExitOnFailure(hr, "Failed to write exit code to message buffer."); | ||
| 543 | |||
| 544 | hr = BuffWriteNumber(&pbData, &cbData, fRestart); | ||
| 545 | ExitOnFailure(hr, "Failed to write restart to message buffer."); | ||
| 546 | |||
| 547 | // Send the messages. | ||
| 548 | if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) | ||
| 549 | { | ||
| 550 | hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); | ||
| 551 | ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); | ||
| 552 | } | ||
| 553 | |||
| 554 | hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); | ||
| 555 | ExitOnFailure(hr, "Failed to post terminate message to child process."); | ||
| 556 | |||
| 557 | // If we were able to get a handle to the other process, wait for it to exit. | ||
| 558 | if (pConnection->hProcess) | ||
| 559 | { | ||
| 560 | if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION)) | ||
| 561 | { | ||
| 562 | ExitWithLastError(hr, "Failed to wait for child process exit."); | ||
| 563 | } | ||
| 564 | |||
| 565 | #ifdef DEBUG | ||
| 566 | DWORD dwChildExitCode = 0; | ||
| 567 | DWORD dwErrorCode = ERROR_SUCCESS; | ||
| 568 | BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode); | ||
| 569 | if (!fReturnedExitCode) | ||
| 570 | { | ||
| 571 | dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. | ||
| 572 | |||
| 573 | // The unit test use a thread instead of a process so try to get the exit code from | ||
| 574 | // the thread because we failed to get it from the process. | ||
| 575 | if (ERROR_INVALID_HANDLE == dwErrorCode) | ||
| 576 | { | ||
| 577 | fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode); | ||
| 578 | } | ||
| 579 | } | ||
| 580 | AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) || | ||
| 581 | (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode), | ||
| 582 | "Child elevated process did not return matching exit code to parent process."); | ||
| 583 | #endif | ||
| 584 | } | ||
| 585 | |||
| 586 | LExit: | ||
| 587 | return hr; | ||
| 588 | } | ||
| 589 | |||
| 590 | /******************************************************************* | ||
| 591 | PipeChildConnect - Called from the child process to connect back | ||
| 592 | to the pipe provided by the parent process. | ||
| 593 | |||
| 594 | *******************************************************************/ | ||
| 595 | extern "C" HRESULT PipeChildConnect( | ||
| 596 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 597 | __in BOOL fConnectCachePipe | ||
| 598 | ) | ||
| 599 | { | ||
| 600 | Assert(pConnection->sczName); | ||
| 601 | Assert(pConnection->sczSecret); | ||
| 602 | Assert(!pConnection->hProcess); | ||
| 603 | Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); | ||
| 604 | Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); | ||
| 605 | |||
| 606 | HRESULT hr = S_OK; | ||
| 607 | LPWSTR sczPipeName = NULL; | ||
| 608 | |||
| 609 | // Try to connect to the parent. | ||
| 610 | hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); | ||
| 611 | ExitOnFailure(hr, "Failed to allocate name of parent pipe."); | ||
| 612 | |||
| 613 | hr = E_UNEXPECTED; | ||
| 614 | for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) | ||
| 615 | { | ||
| 616 | pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); | ||
| 617 | if (INVALID_HANDLE_VALUE == pConnection->hPipe) | ||
| 618 | { | ||
| 619 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 620 | if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. | ||
| 621 | { | ||
| 622 | hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); | ||
| 623 | } | ||
| 624 | |||
| 625 | ::Sleep(PIPE_WAIT_FOR_CONNECTION); | ||
| 626 | } | ||
| 627 | else // we have a connection, go with it. | ||
| 628 | { | ||
| 629 | hr = S_OK; | ||
| 630 | } | ||
| 631 | } | ||
| 632 | ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) | ||
| 633 | |||
| 634 | // Verify the parent and notify it that the child connected. | ||
| 635 | hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); | ||
| 636 | ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); | ||
| 637 | |||
| 638 | if (fConnectCachePipe) | ||
| 639 | { | ||
| 640 | // Connect to the parent for the cache pipe. | ||
| 641 | hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); | ||
| 642 | ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); | ||
| 643 | |||
| 644 | pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); | ||
| 645 | if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) | ||
| 646 | { | ||
| 647 | ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName) | ||
| 648 | } | ||
| 649 | |||
| 650 | // Verify the parent and notify it that the child connected. | ||
| 651 | hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); | ||
| 652 | ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); | ||
| 653 | } | ||
| 654 | |||
| 655 | pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); | ||
| 656 | ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); | ||
| 657 | |||
| 658 | LExit: | ||
| 659 | ReleaseStr(sczPipeName); | ||
| 660 | |||
| 661 | return hr; | ||
| 662 | } | ||
| 663 | |||
| 664 | |||
| 665 | static HRESULT AllocatePipeMessage( | ||
| 666 | __in DWORD dwMessage, | ||
| 667 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 668 | __in DWORD cbData, | ||
| 669 | __out_bcount(cb) LPVOID* ppvMessage, | ||
| 670 | __out DWORD* cbMessage | ||
| 671 | ) | ||
| 672 | { | ||
| 673 | HRESULT hr = S_OK; | ||
| 674 | LPVOID pv = NULL; | ||
| 675 | DWORD cb = 0; | ||
| 676 | |||
| 677 | // If no data was provided, ensure the count of bytes is zero. | ||
| 678 | if (!pvData) | ||
| 679 | { | ||
| 680 | cbData = 0; | ||
| 681 | } | ||
| 682 | |||
| 683 | // Allocate the message. | ||
| 684 | cb = sizeof(dwMessage) + sizeof(cbData) + cbData; | ||
| 685 | pv = MemAlloc(cb, FALSE); | ||
| 686 | ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); | ||
| 687 | |||
| 688 | memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); | ||
| 689 | memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData)); | ||
| 690 | if (cbData) | ||
| 691 | { | ||
| 692 | memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData); | ||
| 693 | } | ||
| 694 | |||
| 695 | *cbMessage = cb; | ||
| 696 | *ppvMessage = pv; | ||
| 697 | pv = NULL; | ||
| 698 | |||
| 699 | LExit: | ||
| 700 | ReleaseMem(pv); | ||
| 701 | return hr; | ||
| 702 | } | ||
| 703 | |||
| 704 | static void FreePipeMessage( | ||
| 705 | __in BURN_PIPE_MESSAGE *pMsg | ||
| 706 | ) | ||
| 707 | { | ||
| 708 | if (pMsg->fAllocatedData) | ||
| 709 | { | ||
| 710 | ReleaseNullMem(pMsg->pvData); | ||
| 711 | pMsg->fAllocatedData = FALSE; | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | static HRESULT WritePipeMessage( | ||
| 716 | __in HANDLE hPipe, | ||
| 717 | __in DWORD dwMessage, | ||
| 718 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 719 | __in DWORD cbData | ||
| 720 | ) | ||
| 721 | { | ||
| 722 | HRESULT hr = S_OK; | ||
| 723 | LPVOID pv = NULL; | ||
| 724 | DWORD cb = 0; | ||
| 725 | |||
| 726 | hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); | ||
| 727 | ExitOnFailure(hr, "Failed to allocate message to write."); | ||
| 728 | |||
| 729 | // Write the message. | ||
| 730 | DWORD cbWrote = 0; | ||
| 731 | DWORD cbTotalWritten = 0; | ||
| 732 | while (cbTotalWritten < cb) | ||
| 733 | { | ||
| 734 | if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL)) | ||
| 735 | { | ||
| 736 | ExitWithLastError(hr, "Failed to write message type to pipe."); | ||
| 737 | } | ||
| 738 | |||
| 739 | cbTotalWritten += cbWrote; | ||
| 740 | } | ||
| 741 | |||
| 742 | LExit: | ||
| 743 | ReleaseMem(pv); | ||
| 744 | return hr; | ||
| 745 | } | ||
| 746 | |||
| 747 | static HRESULT GetPipeMessage( | ||
| 748 | __in HANDLE hPipe, | ||
| 749 | __in BURN_PIPE_MESSAGE* pMsg | ||
| 750 | ) | ||
| 751 | { | ||
| 752 | HRESULT hr = S_OK; | ||
| 753 | DWORD rgdwMessageAndByteCount[2] = { }; | ||
| 754 | DWORD cb = 0; | ||
| 755 | DWORD cbRead = 0; | ||
| 756 | |||
| 757 | while (cbRead < sizeof(rgdwMessageAndByteCount)) | ||
| 758 | { | ||
| 759 | if (!::ReadFile(hPipe, reinterpret_cast<BYTE*>(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL)) | ||
| 760 | { | ||
| 761 | DWORD er = ::GetLastError(); | ||
| 762 | if (ERROR_MORE_DATA == er) | ||
| 763 | { | ||
| 764 | hr = S_OK; | ||
| 765 | } | ||
| 766 | else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit. | ||
| 767 | { | ||
| 768 | memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount)); | ||
| 769 | hr = S_FALSE; | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | else | ||
| 773 | { | ||
| 774 | hr = HRESULT_FROM_WIN32(er); | ||
| 775 | } | ||
| 776 | ExitOnRootFailure(hr, "Failed to read message from pipe."); | ||
| 777 | } | ||
| 778 | |||
| 779 | cbRead += cb; | ||
| 780 | } | ||
| 781 | |||
| 782 | pMsg->dwMessage = rgdwMessageAndByteCount[0]; | ||
| 783 | pMsg->cbData = rgdwMessageAndByteCount[1]; | ||
| 784 | if (pMsg->cbData) | ||
| 785 | { | ||
| 786 | pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); | ||
| 787 | ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); | ||
| 788 | |||
| 789 | if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL)) | ||
| 790 | { | ||
| 791 | ExitWithLastError(hr, "Failed to read data for message."); | ||
| 792 | } | ||
| 793 | |||
| 794 | pMsg->fAllocatedData = TRUE; | ||
| 795 | } | ||
| 796 | |||
| 797 | LExit: | ||
| 798 | if (!pMsg->fAllocatedData && pMsg->pvData) | ||
| 799 | { | ||
| 800 | MemFree(pMsg->pvData); | ||
| 801 | } | ||
| 802 | |||
| 803 | return hr; | ||
| 804 | } | ||
| 805 | |||
| 806 | static HRESULT ChildPipeConnected( | ||
| 807 | __in HANDLE hPipe, | ||
| 808 | __in_z LPCWSTR wzSecret, | ||
| 809 | __inout DWORD* pdwProcessId | ||
| 810 | ) | ||
| 811 | { | ||
| 812 | HRESULT hr = S_OK; | ||
| 813 | LPWSTR sczVerificationSecret = NULL; | ||
| 814 | DWORD cbVerificationSecret = 0; | ||
| 815 | DWORD dwVerificationProcessId = 0; | ||
| 816 | DWORD dwRead = 0; | ||
| 817 | DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. | ||
| 818 | DWORD cb = 0; | ||
| 819 | |||
| 820 | // Read the verification secret. | ||
| 821 | if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL)) | ||
| 822 | { | ||
| 823 | ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe."); | ||
| 824 | } | ||
| 825 | |||
| 826 | if (255 < cbVerificationSecret / sizeof(WCHAR)) | ||
| 827 | { | ||
| 828 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 829 | ExitOnRootFailure(hr, "Verification secret from parent is too big."); | ||
| 830 | } | ||
| 831 | |||
| 832 | hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); | ||
| 833 | ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); | ||
| 834 | |||
| 835 | if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL)) | ||
| 836 | { | ||
| 837 | ExitWithLastError(hr, "Failed to read verification secret from parent pipe."); | ||
| 838 | } | ||
| 839 | |||
| 840 | // Verify the secrets match. | ||
| 841 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) | ||
| 842 | { | ||
| 843 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 844 | ExitOnRootFailure(hr, "Verification secret from parent does not match."); | ||
| 845 | } | ||
| 846 | |||
| 847 | // Read the verification process id. | ||
| 848 | if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL)) | ||
| 849 | { | ||
| 850 | ExitWithLastError(hr, "Failed to read verification process id from parent pipe."); | ||
| 851 | } | ||
| 852 | |||
| 853 | // If a process id was not provided, we'll trust the process id from the parent. | ||
| 854 | if (*pdwProcessId == 0) | ||
| 855 | { | ||
| 856 | *pdwProcessId = dwVerificationProcessId; | ||
| 857 | } | ||
| 858 | else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. | ||
| 859 | { | ||
| 860 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 861 | ExitOnRootFailure(hr, "Verification process id from parent does not match."); | ||
| 862 | } | ||
| 863 | |||
| 864 | // All is well, tell the parent process. | ||
| 865 | if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL)) | ||
| 866 | { | ||
| 867 | ExitWithLastError(hr, "Failed to inform parent process that child is running."); | ||
| 868 | } | ||
| 869 | |||
| 870 | LExit: | ||
| 871 | ReleaseStr(sczVerificationSecret); | ||
| 872 | return hr; | ||
| 873 | } | ||
diff --git a/src/engine/pipe.h b/src/engine/pipe.h new file mode 100644 index 00000000..b6a7742a --- /dev/null +++ b/src/engine/pipe.h | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef __cplusplus | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | typedef struct _BURN_PIPE_CONNECTION | ||
| 10 | { | ||
| 11 | LPWSTR sczName; | ||
| 12 | LPWSTR sczSecret; | ||
| 13 | DWORD dwProcessId; | ||
| 14 | |||
| 15 | HANDLE hProcess; | ||
| 16 | HANDLE hPipe; | ||
| 17 | HANDLE hCachePipe; | ||
| 18 | } BURN_PIPE_CONNECTION; | ||
| 19 | |||
| 20 | typedef enum _BURN_PIPE_MESSAGE_TYPE | ||
| 21 | { | ||
| 22 | BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, | ||
| 23 | BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, | ||
| 24 | BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, | ||
| 25 | } BURN_PIPE_MESSAGE_TYPE; | ||
| 26 | |||
| 27 | typedef struct _BURN_PIPE_MESSAGE | ||
| 28 | { | ||
| 29 | DWORD dwMessage; | ||
| 30 | DWORD cbData; | ||
| 31 | |||
| 32 | BOOL fAllocatedData; | ||
| 33 | LPVOID pvData; | ||
| 34 | } BURN_PIPE_MESSAGE; | ||
| 35 | |||
| 36 | typedef struct _BURN_PIPE_RESULT | ||
| 37 | { | ||
| 38 | DWORD dwResult; | ||
| 39 | BOOL fRestart; | ||
| 40 | } BURN_PIPE_RESULT; | ||
| 41 | |||
| 42 | |||
| 43 | typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( | ||
| 44 | __in BURN_PIPE_MESSAGE* pMsg, | ||
| 45 | __in_opt LPVOID pvContext, | ||
| 46 | __out DWORD* pdwResult | ||
| 47 | ); | ||
| 48 | |||
| 49 | |||
| 50 | // Common functions. | ||
| 51 | void PipeConnectionInitialize( | ||
| 52 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 53 | ); | ||
| 54 | void PipeConnectionUninitialize( | ||
| 55 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 56 | ); | ||
| 57 | HRESULT PipeSendMessage( | ||
| 58 | __in HANDLE hPipe, | ||
| 59 | __in DWORD dwMessage, | ||
| 60 | __in_bcount_opt(cbData) LPVOID pvData, | ||
| 61 | __in DWORD cbData, | ||
| 62 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, | ||
| 63 | __in_opt LPVOID pvContext, | ||
| 64 | __out DWORD* pdwResult | ||
| 65 | ); | ||
| 66 | HRESULT PipePumpMessages( | ||
| 67 | __in HANDLE hPipe, | ||
| 68 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, | ||
| 69 | __in_opt LPVOID pvContext, | ||
| 70 | __in BURN_PIPE_RESULT* pResult | ||
| 71 | ); | ||
| 72 | |||
| 73 | // Parent functions. | ||
| 74 | HRESULT PipeCreateNameAndSecret( | ||
| 75 | __out_z LPWSTR *psczConnectionName, | ||
| 76 | __out_z LPWSTR *psczSecret | ||
| 77 | ); | ||
| 78 | HRESULT PipeCreatePipes( | ||
| 79 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 80 | __in BOOL fCreateCachePipe, | ||
| 81 | __out HANDLE* phEvent | ||
| 82 | ); | ||
| 83 | HRESULT PipeLaunchParentProcess( | ||
| 84 | __in LPCWSTR wzCommandLine, | ||
| 85 | __in int nCmdShow, | ||
| 86 | __in_z LPWSTR sczConnectionName, | ||
| 87 | __in_z LPWSTR sczSecret, | ||
| 88 | __in BOOL fDisableUnelevate | ||
| 89 | ); | ||
| 90 | HRESULT PipeLaunchChildProcess( | ||
| 91 | __in_z LPCWSTR wzExecutablePath, | ||
| 92 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 93 | __in BOOL fElevate, | ||
| 94 | __in_opt HWND hwndParent | ||
| 95 | ); | ||
| 96 | HRESULT PipeWaitForChildConnect( | ||
| 97 | __in BURN_PIPE_CONNECTION* pConnection | ||
| 98 | ); | ||
| 99 | HRESULT PipeTerminateChildProcess( | ||
| 100 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 101 | __in DWORD dwParentExitCode, | ||
| 102 | __in BOOL fRestart | ||
| 103 | ); | ||
| 104 | |||
| 105 | // Child functions. | ||
| 106 | HRESULT PipeChildConnect( | ||
| 107 | __in BURN_PIPE_CONNECTION* pConnection, | ||
| 108 | __in BOOL fConnectCachePipe | ||
| 109 | ); | ||
| 110 | |||
| 111 | #ifdef __cplusplus | ||
| 112 | } | ||
| 113 | #endif | ||
diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp new file mode 100644 index 00000000..01c7a31d --- /dev/null +++ b/src/engine/plan.cpp | |||
| @@ -0,0 +1,3169 @@ | |||
| 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 | |||
| 5 | // internal struct definitions | ||
| 6 | |||
| 7 | struct PLAN_NONPERMANENT_PACKAGE_INDICES | ||
| 8 | { | ||
| 9 | DWORD iAfterExecuteFirstNonPermanentPackage; | ||
| 10 | DWORD iBeforeRollbackFirstNonPermanentPackage; | ||
| 11 | DWORD iAfterExecuteLastNonPermanentPackage; | ||
| 12 | DWORD iAfterRollbackLastNonPermanentPackage; | ||
| 13 | }; | ||
| 14 | |||
| 15 | // internal function definitions | ||
| 16 | |||
| 17 | static void UninitializeRegistrationAction( | ||
| 18 | __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 19 | ); | ||
| 20 | static void UninitializeCacheAction( | ||
| 21 | __in BURN_CACHE_ACTION* pCacheAction | ||
| 22 | ); | ||
| 23 | static void ResetPlannedPackageState( | ||
| 24 | __in BURN_PACKAGE* pPackage | ||
| 25 | ); | ||
| 26 | static HRESULT ProcessPackage( | ||
| 27 | __in BOOL fBundlePerMachine, | ||
| 28 | __in BURN_PACKAGE* pCompatiblePackageParent, | ||
| 29 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 30 | __in BURN_PLAN* pPlan, | ||
| 31 | __in BURN_PACKAGE* pPackage, | ||
| 32 | __in BURN_LOGGING* pLog, | ||
| 33 | __in BURN_VARIABLES* pVariables, | ||
| 34 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 35 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 36 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 37 | __inout HANDLE* phSyncpointEvent, | ||
| 38 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
| 39 | __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices | ||
| 40 | ); | ||
| 41 | static HRESULT ProcessPackageRollbackBoundary( | ||
| 42 | __in BURN_PLAN* pPlan, | ||
| 43 | __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, | ||
| 44 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
| 45 | ); | ||
| 46 | static HRESULT GetActionDefaultRequestState( | ||
| 47 | __in BOOTSTRAPPER_ACTION action, | ||
| 48 | __in BOOL fPermanent, | ||
| 49 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
| 50 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
| 51 | ); | ||
| 52 | static HRESULT AddRegistrationAction( | ||
| 53 | __in BURN_PLAN* pPlan, | ||
| 54 | __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, | ||
| 55 | __in_z LPCWSTR wzDependentProviderKey, | ||
| 56 | __in_z LPCWSTR wzOwnerBundleId | ||
| 57 | ); | ||
| 58 | static HRESULT AddCachePackage( | ||
| 59 | __in BURN_PLAN* pPlan, | ||
| 60 | __in BURN_PACKAGE* pPackage, | ||
| 61 | __out HANDLE* phSyncpointEvent | ||
| 62 | ); | ||
| 63 | static HRESULT AddCachePackageHelper( | ||
| 64 | __in BURN_PLAN* pPlan, | ||
| 65 | __in BURN_PACKAGE* pPackage, | ||
| 66 | __out HANDLE* phSyncpointEvent | ||
| 67 | ); | ||
| 68 | static HRESULT AddCacheSlipstreamMsps( | ||
| 69 | __in BURN_PLAN* pPlan, | ||
| 70 | __in BURN_PACKAGE* pPackage | ||
| 71 | ); | ||
| 72 | static BOOL AlreadyPlannedCachePackage( | ||
| 73 | __in BURN_PLAN* pPlan, | ||
| 74 | __in_z LPCWSTR wzPackageId, | ||
| 75 | __out HANDLE* phSyncpointEvent | ||
| 76 | ); | ||
| 77 | static DWORD GetNextCheckpointId(); | ||
| 78 | static HRESULT AppendCacheAction( | ||
| 79 | __in BURN_PLAN* pPlan, | ||
| 80 | __out BURN_CACHE_ACTION** ppCacheAction | ||
| 81 | ); | ||
| 82 | static HRESULT AppendRollbackCacheAction( | ||
| 83 | __in BURN_PLAN* pPlan, | ||
| 84 | __out BURN_CACHE_ACTION** ppCacheAction | ||
| 85 | ); | ||
| 86 | static HRESULT AppendLayoutContainerAction( | ||
| 87 | __in BURN_PLAN* pPlan, | ||
| 88 | __in_opt BURN_PACKAGE* pPackage, | ||
| 89 | __in DWORD iPackageStartAction, | ||
| 90 | __in BURN_CONTAINER* pContainer, | ||
| 91 | __in BOOL fContainerCached, | ||
| 92 | __in_z LPCWSTR wzLayoutDirectory | ||
| 93 | ); | ||
| 94 | static HRESULT AppendCacheOrLayoutPayloadAction( | ||
| 95 | __in BURN_PLAN* pPlan, | ||
| 96 | __in_opt BURN_PACKAGE* pPackage, | ||
| 97 | __in DWORD iPackageStartAction, | ||
| 98 | __in BURN_PAYLOAD* pPayload, | ||
| 99 | __in BOOL fPayloadCached, | ||
| 100 | __in_z_opt LPCWSTR wzLayoutDirectory | ||
| 101 | ); | ||
| 102 | static BOOL FindContainerCacheAction( | ||
| 103 | __in BURN_CACHE_ACTION_TYPE type, | ||
| 104 | __in BURN_PLAN* pPlan, | ||
| 105 | __in BURN_CONTAINER* pContainer, | ||
| 106 | __in DWORD iSearchStart, | ||
| 107 | __in DWORD iSearchEnd, | ||
| 108 | __out_opt BURN_CACHE_ACTION** ppCacheAction, | ||
| 109 | __out_opt DWORD* piCacheAction | ||
| 110 | ); | ||
| 111 | static HRESULT CreateContainerAcquireAndExtractAction( | ||
| 112 | __in BURN_PLAN* pPlan, | ||
| 113 | __in BURN_CONTAINER* pContainer, | ||
| 114 | __in DWORD iPackageStartAction, | ||
| 115 | __in BOOL fPayloadCached, | ||
| 116 | __out BURN_CACHE_ACTION** ppContainerExtractAction, | ||
| 117 | __out DWORD* piContainerTryAgainAction | ||
| 118 | ); | ||
| 119 | static HRESULT AddAcquireContainer( | ||
| 120 | __in BURN_PLAN* pPlan, | ||
| 121 | __in BURN_CONTAINER* pContainer, | ||
| 122 | __out_opt BURN_CACHE_ACTION** ppCacheAction, | ||
| 123 | __out_opt DWORD* piCacheAction | ||
| 124 | ); | ||
| 125 | static HRESULT AddExtractPayload( | ||
| 126 | __in BURN_CACHE_ACTION* pCacheAction, | ||
| 127 | __in_opt BURN_PACKAGE* pPackage, | ||
| 128 | __in BURN_PAYLOAD* pPayload, | ||
| 129 | __in_z LPCWSTR wzPayloadWorkingPath | ||
| 130 | ); | ||
| 131 | static BURN_CACHE_ACTION* ProcessSharedPayload( | ||
| 132 | __in BURN_PLAN* pPlan, | ||
| 133 | __in BURN_PAYLOAD* pPayload | ||
| 134 | ); | ||
| 135 | static HRESULT RemoveUnnecessaryActions( | ||
| 136 | __in BOOL fExecute, | ||
| 137 | __in BURN_EXECUTE_ACTION* rgActions, | ||
| 138 | __in DWORD cActions | ||
| 139 | ); | ||
| 140 | static HRESULT FinalizeSlipstreamPatchActions( | ||
| 141 | __in BOOL fExecute, | ||
| 142 | __in BURN_EXECUTE_ACTION* rgActions, | ||
| 143 | __in DWORD cActions | ||
| 144 | ); | ||
| 145 | static HRESULT PlanDependencyActions( | ||
| 146 | __in BOOL fBundlePerMachine, | ||
| 147 | __in BURN_PLAN* pPlan, | ||
| 148 | __in BURN_PACKAGE* pPackage | ||
| 149 | ); | ||
| 150 | static HRESULT CalculateExecuteActions( | ||
| 151 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 152 | __in BURN_PACKAGE* pPackage, | ||
| 153 | __in BURN_VARIABLES* pVariables, | ||
| 154 | __out_opt BOOL* pfBARequestedCache | ||
| 155 | ); | ||
| 156 | static BOOL NeedsCache( | ||
| 157 | __in BURN_PLAN* pPlan, | ||
| 158 | __in BURN_PACKAGE* pPackage | ||
| 159 | ); | ||
| 160 | static HRESULT CreateContainerProgress( | ||
| 161 | __in BURN_PLAN* pPlan, | ||
| 162 | __in BURN_CONTAINER* pContainer, | ||
| 163 | __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress | ||
| 164 | ); | ||
| 165 | static HRESULT CreatePayloadProgress( | ||
| 166 | __in BURN_PLAN* pPlan, | ||
| 167 | __in BURN_PAYLOAD* pPayload, | ||
| 168 | __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress | ||
| 169 | ); | ||
| 170 | |||
| 171 | // function definitions | ||
| 172 | |||
| 173 | extern "C" void PlanReset( | ||
| 174 | __in BURN_PLAN* pPlan, | ||
| 175 | __in BURN_PACKAGES* pPackages | ||
| 176 | ) | ||
| 177 | { | ||
| 178 | if (pPlan->rgRegistrationActions) | ||
| 179 | { | ||
| 180 | for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i) | ||
| 181 | { | ||
| 182 | UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]); | ||
| 183 | } | ||
| 184 | MemFree(pPlan->rgRegistrationActions); | ||
| 185 | } | ||
| 186 | |||
| 187 | if (pPlan->rgRollbackRegistrationActions) | ||
| 188 | { | ||
| 189 | for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i) | ||
| 190 | { | ||
| 191 | UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]); | ||
| 192 | } | ||
| 193 | MemFree(pPlan->rgRollbackRegistrationActions); | ||
| 194 | } | ||
| 195 | |||
| 196 | if (pPlan->rgCacheActions) | ||
| 197 | { | ||
| 198 | for (DWORD i = 0; i < pPlan->cCacheActions; ++i) | ||
| 199 | { | ||
| 200 | UninitializeCacheAction(&pPlan->rgCacheActions[i]); | ||
| 201 | } | ||
| 202 | MemFree(pPlan->rgCacheActions); | ||
| 203 | } | ||
| 204 | |||
| 205 | if (pPlan->rgExecuteActions) | ||
| 206 | { | ||
| 207 | for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) | ||
| 208 | { | ||
| 209 | PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]); | ||
| 210 | } | ||
| 211 | MemFree(pPlan->rgExecuteActions); | ||
| 212 | } | ||
| 213 | |||
| 214 | if (pPlan->rgRollbackActions) | ||
| 215 | { | ||
| 216 | for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) | ||
| 217 | { | ||
| 218 | PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]); | ||
| 219 | } | ||
| 220 | MemFree(pPlan->rgRollbackActions); | ||
| 221 | } | ||
| 222 | |||
| 223 | if (pPlan->rgCleanActions) | ||
| 224 | { | ||
| 225 | // Nothing needs to be freed inside clean actions today. | ||
| 226 | MemFree(pPlan->rgCleanActions); | ||
| 227 | } | ||
| 228 | |||
| 229 | if (pPlan->rgPlannedProviders) | ||
| 230 | { | ||
| 231 | ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders); | ||
| 232 | } | ||
| 233 | |||
| 234 | if (pPlan->rgContainerProgress) | ||
| 235 | { | ||
| 236 | MemFree(pPlan->rgContainerProgress); | ||
| 237 | } | ||
| 238 | |||
| 239 | if (pPlan->shContainerProgress) | ||
| 240 | { | ||
| 241 | ReleaseDict(pPlan->shContainerProgress); | ||
| 242 | } | ||
| 243 | |||
| 244 | if (pPlan->rgPayloadProgress) | ||
| 245 | { | ||
| 246 | MemFree(pPlan->rgPayloadProgress); | ||
| 247 | } | ||
| 248 | |||
| 249 | if (pPlan->shPayloadProgress) | ||
| 250 | { | ||
| 251 | ReleaseDict(pPlan->shPayloadProgress); | ||
| 252 | } | ||
| 253 | |||
| 254 | memset(pPlan, 0, sizeof(BURN_PLAN)); | ||
| 255 | |||
| 256 | // Reset the planned actions for each package. | ||
| 257 | if (pPackages->rgPackages) | ||
| 258 | { | ||
| 259 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 260 | { | ||
| 261 | ResetPlannedPackageState(&pPackages->rgPackages[i]); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | extern "C" void PlanUninitializeExecuteAction( | ||
| 267 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 268 | ) | ||
| 269 | { | ||
| 270 | switch (pExecuteAction->type) | ||
| 271 | { | ||
| 272 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
| 273 | ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); | ||
| 274 | ReleaseStr(pExecuteAction->exePackage.sczAncestors); | ||
| 275 | break; | ||
| 276 | |||
| 277 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
| 278 | ReleaseStr(pExecuteAction->msiPackage.sczLogPath); | ||
| 279 | ReleaseMem(pExecuteAction->msiPackage.rgFeatures); | ||
| 280 | ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches); | ||
| 281 | ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches); | ||
| 282 | break; | ||
| 283 | |||
| 284 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
| 285 | ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode); | ||
| 286 | ReleaseStr(pExecuteAction->mspTarget.sczLogPath); | ||
| 287 | ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches); | ||
| 288 | break; | ||
| 289 | |||
| 290 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
| 291 | ReleaseStr(pExecuteAction->msuPackage.sczLogPath); | ||
| 292 | break; | ||
| 293 | |||
| 294 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough; | ||
| 295 | case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: | ||
| 296 | ReleaseStr(pExecuteAction->service.sczServiceName); | ||
| 297 | break; | ||
| 298 | |||
| 299 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
| 300 | ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); | ||
| 301 | break; | ||
| 302 | |||
| 303 | case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: | ||
| 304 | ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode); | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | extern "C" HRESULT PlanSetVariables( | ||
| 310 | __in BOOTSTRAPPER_ACTION action, | ||
| 311 | __in BURN_VARIABLES* pVariables | ||
| 312 | ) | ||
| 313 | { | ||
| 314 | HRESULT hr = S_OK; | ||
| 315 | |||
| 316 | hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); | ||
| 317 | ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); | ||
| 318 | |||
| 319 | LExit: | ||
| 320 | return hr; | ||
| 321 | } | ||
| 322 | |||
| 323 | extern "C" HRESULT PlanDefaultPackageRequestState( | ||
| 324 | __in BURN_PACKAGE_TYPE packageType, | ||
| 325 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
| 326 | __in BOOL fPermanent, | ||
| 327 | __in BOOTSTRAPPER_ACTION action, | ||
| 328 | __in BURN_VARIABLES* pVariables, | ||
| 329 | __in_z_opt LPCWSTR wzInstallCondition, | ||
| 330 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 331 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
| 332 | ) | ||
| 333 | { | ||
| 334 | HRESULT hr = S_OK; | ||
| 335 | BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 336 | BOOL fCondition = FALSE; | ||
| 337 | |||
| 338 | // If doing layout, then always default to requesting the file be cached. | ||
| 339 | if (BOOTSTRAPPER_ACTION_LAYOUT == action) | ||
| 340 | { | ||
| 341 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; | ||
| 342 | } | ||
| 343 | else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType) | ||
| 344 | { | ||
| 345 | // For patch related bundles, only install a patch if currently absent during install, modify, or repair. | ||
| 346 | if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action) | ||
| 347 | { | ||
| 348 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; | ||
| 349 | } | ||
| 350 | else | ||
| 351 | { | ||
| 352 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 353 | } | ||
| 354 | } | ||
| 355 | else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) | ||
| 356 | { | ||
| 357 | // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. | ||
| 358 | // All other operations do nothing. | ||
| 359 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 360 | } | ||
| 361 | else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) | ||
| 362 | { | ||
| 363 | // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete | ||
| 364 | // and present so allow them to be removed during uninstall. Everyone else, gets nothing. | ||
| 365 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 366 | } | ||
| 367 | else // pick the best option for the action state and install condition. | ||
| 368 | { | ||
| 369 | hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); | ||
| 370 | ExitOnFailure(hr, "Failed to get default request state for action."); | ||
| 371 | |||
| 372 | // If there is an install condition (and we're doing an install) evaluate the condition | ||
| 373 | // to determine whether to use the default request state or make the package absent. | ||
| 374 | if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition) | ||
| 375 | { | ||
| 376 | hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition); | ||
| 377 | ExitOnFailure(hr, "Failed to evaluate install condition."); | ||
| 378 | |||
| 379 | *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT; | ||
| 380 | } | ||
| 381 | else // just set the package to the default request state. | ||
| 382 | { | ||
| 383 | *pRequestState = defaultRequestState; | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | LExit: | ||
| 388 | return hr; | ||
| 389 | } | ||
| 390 | |||
| 391 | extern "C" HRESULT PlanLayoutBundle( | ||
| 392 | __in BURN_PLAN* pPlan, | ||
| 393 | __in_z LPCWSTR wzExecutableName, | ||
| 394 | __in DWORD64 qwBundleSize, | ||
| 395 | __in BURN_VARIABLES* pVariables, | ||
| 396 | __in BURN_PAYLOADS* pPayloads, | ||
| 397 | __out_z LPWSTR* psczLayoutDirectory | ||
| 398 | ) | ||
| 399 | { | ||
| 400 | HRESULT hr = S_OK; | ||
| 401 | BURN_CACHE_ACTION* pCacheAction = NULL; | ||
| 402 | LPWSTR sczExecutablePath = NULL; | ||
| 403 | LPWSTR sczLayoutDirectory = NULL; | ||
| 404 | |||
| 405 | // Get the layout directory. | ||
| 406 | hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory); | ||
| 407 | if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. | ||
| 408 | { | ||
| 409 | hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory); | ||
| 410 | if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory. | ||
| 411 | { | ||
| 412 | hr = PathForCurrentProcess(&sczExecutablePath, NULL); | ||
| 413 | ExitOnFailure(hr, "Failed to get path for current executing process as layout directory."); | ||
| 414 | |||
| 415 | hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory); | ||
| 416 | ExitOnFailure(hr, "Failed to get executing process as layout directory."); | ||
| 417 | } | ||
| 418 | } | ||
| 419 | ExitOnFailure(hr, "Failed to get bundle layout directory property."); | ||
| 420 | |||
| 421 | hr = PathBackslashTerminate(&sczLayoutDirectory); | ||
| 422 | ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated."); | ||
| 423 | |||
| 424 | // Plan the layout of the bundle engine itself. | ||
| 425 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 426 | ExitOnFailure(hr, "Failed to append bundle start action."); | ||
| 427 | |||
| 428 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE; | ||
| 429 | |||
| 430 | hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0); | ||
| 431 | ExitOnFailure(hr, "Failed to to copy executable name for bundle."); | ||
| 432 | |||
| 433 | hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0); | ||
| 434 | ExitOnFailure(hr, "Failed to to copy layout directory for bundle."); | ||
| 435 | |||
| 436 | hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath); | ||
| 437 | ExitOnFailure(hr, "Failed to calculate bundle layout working path."); | ||
| 438 | |||
| 439 | pCacheAction->bundleLayout.qwBundleSize = qwBundleSize; | ||
| 440 | |||
| 441 | pPlan->qwCacheSizeTotal += qwBundleSize; | ||
| 442 | |||
| 443 | ++pPlan->cOverallProgressTicksTotal; | ||
| 444 | |||
| 445 | // Plan the layout of layout-only payloads. | ||
| 446 | for (DWORD i = 0; i < pPayloads->cPayloads; ++i) | ||
| 447 | { | ||
| 448 | BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i; | ||
| 449 | if (pPayload->fLayoutOnly) | ||
| 450 | { | ||
| 451 | // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached | ||
| 452 | // (instead of always FALSE). | ||
| 453 | hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory); | ||
| 454 | ExitOnFailure(hr, "Failed to plan layout payload."); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | *psczLayoutDirectory = sczLayoutDirectory; | ||
| 459 | sczLayoutDirectory = NULL; | ||
| 460 | |||
| 461 | LExit: | ||
| 462 | ReleaseStr(sczLayoutDirectory); | ||
| 463 | ReleaseStr(sczExecutablePath); | ||
| 464 | |||
| 465 | return hr; | ||
| 466 | } | ||
| 467 | |||
| 468 | extern "C" HRESULT PlanPackages( | ||
| 469 | __in BURN_REGISTRATION* pRegistration, | ||
| 470 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 471 | __in BURN_PACKAGES* pPackages, | ||
| 472 | __in BURN_PLAN* pPlan, | ||
| 473 | __in BURN_LOGGING* pLog, | ||
| 474 | __in BURN_VARIABLES* pVariables, | ||
| 475 | __in BOOL fBundleInstalled, | ||
| 476 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 477 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 478 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 479 | __inout HANDLE* phSyncpointEvent | ||
| 480 | ) | ||
| 481 | { | ||
| 482 | HRESULT hr = S_OK; | ||
| 483 | BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. | ||
| 484 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
| 485 | |||
| 486 | PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices; | ||
| 487 | nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 488 | nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 489 | nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 490 | nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 491 | |||
| 492 | // Plan the packages. | ||
| 493 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 494 | { | ||
| 495 | DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; | ||
| 496 | BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; | ||
| 497 | |||
| 498 | // Support passing Ancestors to embedded burn bundles | ||
| 499 | if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) | ||
| 500 | { | ||
| 501 | // Pass along any ancestors and ourself to prevent infinite loops. | ||
| 502 | if (pRegistration->sczAncestors && *pRegistration->sczAncestors) | ||
| 503 | { | ||
| 504 | hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); | ||
| 505 | ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); | ||
| 506 | } | ||
| 507 | else | ||
| 508 | { | ||
| 509 | hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0); | ||
| 510 | ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); | ||
| 515 | ExitOnFailure(hr, "Failed to process package."); | ||
| 516 | |||
| 517 | // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source. | ||
| 518 | if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled) | ||
| 519 | { | ||
| 520 | BURN_PACKAGE* pCompatiblePackage = NULL; | ||
| 521 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 522 | |||
| 523 | // Add the compatible package to the list. | ||
| 524 | hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage); | ||
| 525 | ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId); | ||
| 526 | |||
| 527 | // Plan to load the compatible package into the elevated engine before its needed. | ||
| 528 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 529 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 530 | |||
| 531 | pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE; | ||
| 532 | pAction->compatiblePackage.pReferencePackage = pPackage; | ||
| 533 | pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion; | ||
| 534 | |||
| 535 | hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0); | ||
| 536 | ExitOnFailure(hr, "Failed to copy installed ProductCode"); | ||
| 537 | |||
| 538 | // Process the compatible MSI package like any other. | ||
| 539 | hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices); | ||
| 540 | ExitOnFailure(hr, "Failed to process compatible package."); | ||
| 541 | |||
| 542 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute) | ||
| 543 | { | ||
| 544 | LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode); | ||
| 545 | } | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively. | ||
| 550 | if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)) | ||
| 551 | { | ||
| 552 | if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage) | ||
| 553 | { | ||
| 554 | nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions; | ||
| 555 | nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; | ||
| 556 | } | ||
| 557 | |||
| 558 | hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage); | ||
| 559 | ExitOnFailure(hr, "Failed to plan install keep registration."); | ||
| 560 | } | ||
| 561 | else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 562 | { | ||
| 563 | if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage) | ||
| 564 | { | ||
| 565 | nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; | ||
| 566 | nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; | ||
| 567 | } | ||
| 568 | |||
| 569 | hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage); | ||
| 570 | ExitOnFailure(hr, "Failed to plan uninstall remove registration."); | ||
| 571 | } | ||
| 572 | |||
| 573 | // If we still have an open rollback boundary, complete it. | ||
| 574 | if (pRollbackBoundary) | ||
| 575 | { | ||
| 576 | hr = PlanRollbackBoundaryComplete(pPlan); | ||
| 577 | ExitOnFailure(hr, "Failed to plan rollback boundary begin."); | ||
| 578 | |||
| 579 | pRollbackBoundary = NULL; | ||
| 580 | } | ||
| 581 | |||
| 582 | // Plan clean up of packages. | ||
| 583 | for (DWORD i = 0; i < pPackages->cPackages; ++i) | ||
| 584 | { | ||
| 585 | DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i; | ||
| 586 | BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; | ||
| 587 | |||
| 588 | hr = PlanCleanPackage(pPlan, pPackage); | ||
| 589 | ExitOnFailure(hr, "Failed to plan clean package."); | ||
| 590 | } | ||
| 591 | |||
| 592 | // Plan best-effort clean up of compatible packages. | ||
| 593 | for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i) | ||
| 594 | { | ||
| 595 | DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i; | ||
| 596 | BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage; | ||
| 597 | |||
| 598 | PlanCleanPackage(pPlan, pCompatiblePackage); | ||
| 599 | } | ||
| 600 | |||
| 601 | LExit: | ||
| 602 | return hr; | ||
| 603 | } | ||
| 604 | |||
| 605 | extern "C" HRESULT PlanRegistration( | ||
| 606 | __in BURN_PLAN* pPlan, | ||
| 607 | __in BURN_REGISTRATION* pRegistration, | ||
| 608 | __in BOOTSTRAPPER_RESUME_TYPE resumeType, | ||
| 609 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 610 | __in_z_opt LPCWSTR wzIgnoreDependencies, | ||
| 611 | __out BOOL* pfContinuePlanning | ||
| 612 | ) | ||
| 613 | { | ||
| 614 | HRESULT hr = S_OK; | ||
| 615 | LPCWSTR wzSelfDependent = NULL; | ||
| 616 | STRINGDICT_HANDLE sdIgnoreDependents = NULL; | ||
| 617 | DEPENDENCY* rgDependencies = NULL; | ||
| 618 | UINT cDependencies = 0; | ||
| 619 | |||
| 620 | pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state. | ||
| 621 | |||
| 622 | // Keep the registration if the bundle was already installed or we are planning after a restart. | ||
| 623 | pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType); | ||
| 624 | |||
| 625 | pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed | ||
| 626 | |||
| 627 | // If no parent was specified at all, use the bundle id as the self dependent. | ||
| 628 | if (!pRegistration->sczActiveParent) | ||
| 629 | { | ||
| 630 | wzSelfDependent = pRegistration->sczId; | ||
| 631 | } | ||
| 632 | else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent. | ||
| 633 | { | ||
| 634 | wzSelfDependent = pRegistration->sczActiveParent; | ||
| 635 | } | ||
| 636 | // else parent:none was used which means we should not register a dependency on ourself. | ||
| 637 | |||
| 638 | if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 639 | { | ||
| 640 | // If our provider key was detected and it points to our current bundle then we can | ||
| 641 | // unregister the bundle dependency. | ||
| 642 | if (pRegistration->sczDetectedProviderKeyBundleId && | ||
| 643 | CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1)) | ||
| 644 | { | ||
| 645 | pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER; | ||
| 646 | } | ||
| 647 | else // log that another bundle already owned our registration, hopefully this only happens when a newer version | ||
| 648 | { // of a bundle installed and is in the process of upgrading us. | ||
| 649 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId); | ||
| 650 | } | ||
| 651 | |||
| 652 | // Create the dictionary of dependents that should be ignored. | ||
| 653 | hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE); | ||
| 654 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
| 655 | |||
| 656 | // If the self-dependent dependent exists, plan its removal. If we did not do this, we | ||
| 657 | // would prevent self-removal. | ||
| 658 | if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent)) | ||
| 659 | { | ||
| 660 | hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId); | ||
| 661 | ExitOnFailure(hr, "Failed to allocate registration action."); | ||
| 662 | |||
| 663 | hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent); | ||
| 664 | ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents."); | ||
| 665 | } | ||
| 666 | |||
| 667 | // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning. | ||
| 668 | // However, when being upgraded, we always execute our uninstall because a newer version of us is probably | ||
| 669 | // already on the machine and we need to clean up the stuff specific to this bundle. | ||
| 670 | if (BOOTSTRAPPER_RELATION_UPGRADE != relationType) | ||
| 671 | { | ||
| 672 | // If there were other dependencies to ignore, add them. | ||
| 673 | if (wzIgnoreDependencies && *wzIgnoreDependencies) | ||
| 674 | { | ||
| 675 | hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies); | ||
| 676 | ExitOnFailure(hr, "Failed to add dependents ignored from command-line."); | ||
| 677 | } | ||
| 678 | |||
| 679 | // For addon or patch bundles, dependent related bundles should be ignored. This allows | ||
| 680 | // that addon or patch to be removed even though bundles it targets still are registered. | ||
| 681 | for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) | ||
| 682 | { | ||
| 683 | const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; | ||
| 684 | |||
| 685 | if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) | ||
| 686 | { | ||
| 687 | for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) | ||
| 688 | { | ||
| 689 | const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; | ||
| 690 | |||
| 691 | hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey); | ||
| 692 | ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents."); | ||
| 693 | } | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall. | ||
| 698 | hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies); | ||
| 699 | if (E_FILENOTFOUND == hr) | ||
| 700 | { | ||
| 701 | hr = S_OK; | ||
| 702 | } | ||
| 703 | else if (SUCCEEDED(hr) && cDependencies) | ||
| 704 | { | ||
| 705 | // TODO: callback to the BA and let it have the option to ignore any of these dependents? | ||
| 706 | |||
| 707 | pPlan->fDisallowRemoval = TRUE; // ensure the registration stays | ||
| 708 | *pfContinuePlanning = FALSE; // skip the rest of planning. | ||
| 709 | |||
| 710 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies); | ||
| 711 | } | ||
| 712 | ExitOnFailure(hr, "Failed to check for remaining dependents during planning."); | ||
| 713 | } | ||
| 714 | } | ||
| 715 | else | ||
| 716 | { | ||
| 717 | BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes); | ||
| 718 | |||
| 719 | // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached. | ||
| 720 | if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL)) | ||
| 721 | { | ||
| 722 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; | ||
| 723 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; | ||
| 724 | } | ||
| 725 | else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache. | ||
| 726 | { | ||
| 727 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE; | ||
| 728 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; | ||
| 729 | } | ||
| 730 | else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up". | ||
| 731 | { | ||
| 732 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION; | ||
| 733 | } | ||
| 734 | |||
| 735 | // Always update our estimated size registration when installing/modify/repair since things | ||
| 736 | // may have been added or removed or it just needs to be "fixed up". | ||
| 737 | pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE; | ||
| 738 | |||
| 739 | // Always plan to write our provider key registration when installing/modify/repair to "fix it" | ||
| 740 | // if broken. | ||
| 741 | pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER; | ||
| 742 | |||
| 743 | // Register each dependent related bundle. The ensures that addons and patches are reference | ||
| 744 | // counted and stick around until the last targeted bundle is removed. | ||
| 745 | for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) | ||
| 746 | { | ||
| 747 | const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; | ||
| 748 | |||
| 749 | if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType) | ||
| 750 | { | ||
| 751 | for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j) | ||
| 752 | { | ||
| 753 | const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j; | ||
| 754 | |||
| 755 | if (!DependencyDependentExists(pRegistration, pProvider->sczKey)) | ||
| 756 | { | ||
| 757 | hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId); | ||
| 758 | ExitOnFailure(hr, "Failed to add registration action for dependent related bundle."); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was | ||
| 765 | // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter | ||
| 766 | // as our own dependent. | ||
| 767 | if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle)) | ||
| 768 | { | ||
| 769 | if (!DependencyDependentExists(pRegistration, wzSelfDependent)) | ||
| 770 | { | ||
| 771 | hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId); | ||
| 772 | ExitOnFailure(hr, "Failed to add registration action for self dependent."); | ||
| 773 | } | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | LExit: | ||
| 778 | ReleaseDict(sdIgnoreDependents); | ||
| 779 | ReleaseDependencyArray(rgDependencies, cDependencies); | ||
| 780 | |||
| 781 | return hr; | ||
| 782 | } | ||
| 783 | |||
| 784 | extern "C" HRESULT PlanPassThroughBundle( | ||
| 785 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 786 | __in BURN_PACKAGE* pPackage, | ||
| 787 | __in BURN_PLAN* pPlan, | ||
| 788 | __in BURN_LOGGING* pLog, | ||
| 789 | __in BURN_VARIABLES* pVariables, | ||
| 790 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 791 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 792 | __inout HANDLE* phSyncpointEvent | ||
| 793 | ) | ||
| 794 | { | ||
| 795 | HRESULT hr = S_OK; | ||
| 796 | BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. | ||
| 797 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
| 798 | |||
| 799 | // Plan passthrough package. | ||
| 800 | hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); | ||
| 801 | ExitOnFailure(hr, "Failed to process passthrough package."); | ||
| 802 | |||
| 803 | // If we still have an open rollback boundary, complete it. | ||
| 804 | if (pRollbackBoundary) | ||
| 805 | { | ||
| 806 | hr = PlanRollbackBoundaryComplete(pPlan); | ||
| 807 | ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package."); | ||
| 808 | } | ||
| 809 | |||
| 810 | // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages | ||
| 811 | // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't | ||
| 812 | // need to plan clean up. | ||
| 813 | |||
| 814 | LExit: | ||
| 815 | return hr; | ||
| 816 | } | ||
| 817 | |||
| 818 | extern "C" HRESULT PlanUpdateBundle( | ||
| 819 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 820 | __in BURN_PACKAGE* pPackage, | ||
| 821 | __in BURN_PLAN* pPlan, | ||
| 822 | __in BURN_LOGGING* pLog, | ||
| 823 | __in BURN_VARIABLES* pVariables, | ||
| 824 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 825 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 826 | __inout HANDLE* phSyncpointEvent | ||
| 827 | ) | ||
| 828 | { | ||
| 829 | HRESULT hr = S_OK; | ||
| 830 | BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine. | ||
| 831 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; | ||
| 832 | |||
| 833 | // Plan update package. | ||
| 834 | hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL); | ||
| 835 | ExitOnFailure(hr, "Failed to process update package."); | ||
| 836 | |||
| 837 | // If we still have an open rollback boundary, complete it. | ||
| 838 | if (pRollbackBoundary) | ||
| 839 | { | ||
| 840 | hr = PlanRollbackBoundaryComplete(pPlan); | ||
| 841 | ExitOnFailure(hr, "Failed to plan rollback boundary for update package."); | ||
| 842 | } | ||
| 843 | |||
| 844 | // Plan clean up of update package. | ||
| 845 | hr = PlanCleanPackage(pPlan, pPackage); | ||
| 846 | ExitOnFailure(hr, "Failed to plan clean of update package."); | ||
| 847 | |||
| 848 | LExit: | ||
| 849 | return hr; | ||
| 850 | } | ||
| 851 | |||
| 852 | static HRESULT ProcessPackage( | ||
| 853 | __in BOOL fBundlePerMachine, | ||
| 854 | __in BURN_PACKAGE* pCompatiblePackageParent, | ||
| 855 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 856 | __in BURN_PLAN* pPlan, | ||
| 857 | __in BURN_PACKAGE* pPackage, | ||
| 858 | __in BURN_LOGGING* pLog, | ||
| 859 | __in BURN_VARIABLES* pVariables, | ||
| 860 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 861 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 862 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 863 | __inout HANDLE* phSyncpointEvent, | ||
| 864 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary, | ||
| 865 | __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices | ||
| 866 | ) | ||
| 867 | { | ||
| 868 | HRESULT hr = S_OK; | ||
| 869 | BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; | ||
| 870 | BOOL fPlanPackageBegan = FALSE; | ||
| 871 | |||
| 872 | // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. | ||
| 873 | hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); | ||
| 874 | ExitOnFailure(hr, "Failed to set default package state."); | ||
| 875 | |||
| 876 | pPackage->requested = pPackage->defaultRequested; | ||
| 877 | fPlanPackageBegan = TRUE; | ||
| 878 | |||
| 879 | if (pCompatiblePackageParent) | ||
| 880 | { | ||
| 881 | AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages."); | ||
| 882 | |||
| 883 | hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested); | ||
| 884 | ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin."); | ||
| 885 | } | ||
| 886 | else | ||
| 887 | { | ||
| 888 | hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested); | ||
| 889 | ExitOnRootFailure(hr, "BA aborted plan package begin."); | ||
| 890 | } | ||
| 891 | |||
| 892 | pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward; | ||
| 893 | hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary); | ||
| 894 | ExitOnFailure(hr, "Failed to process package rollback boundary."); | ||
| 895 | |||
| 896 | // If the package is in a requested state, plan it. | ||
| 897 | if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) | ||
| 898 | { | ||
| 899 | if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action) | ||
| 900 | { | ||
| 901 | hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory); | ||
| 902 | ExitOnFailure(hr, "Failed to plan layout package."); | ||
| 903 | } | ||
| 904 | else | ||
| 905 | { | ||
| 906 | if (pPackage->fUninstallable && pNonpermanentPackageIndices) | ||
| 907 | { | ||
| 908 | if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage) | ||
| 909 | { | ||
| 910 | pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions; | ||
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent); | ||
| 915 | ExitOnFailure(hr, "Failed to plan execute package."); | ||
| 916 | |||
| 917 | if (pPackage->fUninstallable && pNonpermanentPackageIndices) | ||
| 918 | { | ||
| 919 | if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage) | ||
| 920 | { | ||
| 921 | pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1; | ||
| 922 | } | ||
| 923 | |||
| 924 | pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions; | ||
| 925 | pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions; | ||
| 926 | } | ||
| 927 | } | ||
| 928 | } | ||
| 929 | else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action) | ||
| 930 | { | ||
| 931 | // All packages that have cacheType set to always should be cached if the bundle is going to be present. | ||
| 932 | if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) | ||
| 933 | { | ||
| 934 | hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent); | ||
| 935 | ExitOnFailure(hr, "Failed to plan cache package."); | ||
| 936 | } | ||
| 937 | else | ||
| 938 | { | ||
| 939 | // Make sure the package is properly ref-counted even if no plan is requested. | ||
| 940 | hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage); | ||
| 941 | ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); | ||
| 942 | } | ||
| 943 | } | ||
| 944 | |||
| 945 | // Add the checkpoint after each package and dependency registration action. | ||
| 946 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute) | ||
| 947 | { | ||
| 948 | hr = PlanExecuteCheckpoint(pPlan); | ||
| 949 | ExitOnFailure(hr, "Failed to append execute checkpoint."); | ||
| 950 | } | ||
| 951 | |||
| 952 | LExit: | ||
| 953 | if (fPlanPackageBegan) | ||
| 954 | { | ||
| 955 | if (pCompatiblePackageParent) | ||
| 956 | { | ||
| 957 | UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); | ||
| 958 | } | ||
| 959 | else | ||
| 960 | { | ||
| 961 | UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback); | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | return hr; | ||
| 966 | } | ||
| 967 | |||
| 968 | static HRESULT ProcessPackageRollbackBoundary( | ||
| 969 | __in BURN_PLAN* pPlan, | ||
| 970 | __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, | ||
| 971 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
| 972 | ) | ||
| 973 | { | ||
| 974 | HRESULT hr = S_OK; | ||
| 975 | |||
| 976 | // If the package marks the start of a rollback boundary, start a new one. | ||
| 977 | if (pEffectiveRollbackBoundary) | ||
| 978 | { | ||
| 979 | // Complete previous rollback boundary. | ||
| 980 | if (*ppRollbackBoundary) | ||
| 981 | { | ||
| 982 | hr = PlanRollbackBoundaryComplete(pPlan); | ||
| 983 | ExitOnFailure(hr, "Failed to plan rollback boundary complete."); | ||
| 984 | } | ||
| 985 | |||
| 986 | // Start new rollback boundary. | ||
| 987 | hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary); | ||
| 988 | ExitOnFailure(hr, "Failed to plan rollback boundary begin."); | ||
| 989 | |||
| 990 | *ppRollbackBoundary = pEffectiveRollbackBoundary; | ||
| 991 | } | ||
| 992 | |||
| 993 | LExit: | ||
| 994 | return hr; | ||
| 995 | } | ||
| 996 | |||
| 997 | extern "C" HRESULT PlanLayoutPackage( | ||
| 998 | __in BURN_PLAN* pPlan, | ||
| 999 | __in BURN_PACKAGE* pPackage, | ||
| 1000 | __in_z_opt LPCWSTR wzLayoutDirectory | ||
| 1001 | ) | ||
| 1002 | { | ||
| 1003 | HRESULT hr = S_OK; | ||
| 1004 | BURN_CACHE_ACTION* pCacheAction = NULL; | ||
| 1005 | DWORD iPackageStartAction = 0; | ||
| 1006 | |||
| 1007 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 1008 | ExitOnFailure(hr, "Failed to append package start action."); | ||
| 1009 | |||
| 1010 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; | ||
| 1011 | pCacheAction->packageStart.pPackage = pPackage; | ||
| 1012 | |||
| 1013 | // Remember the index for the package start action (which is now the last in the cache | ||
| 1014 | // actions array) because the array may be resized later and move around in memory. | ||
| 1015 | iPackageStartAction = pPlan->cCacheActions - 1; | ||
| 1016 | |||
| 1017 | // If any of the package payloads are not cached, add them to the plan. | ||
| 1018 | for (DWORD i = 0; i < pPackage->cPayloads; ++i) | ||
| 1019 | { | ||
| 1020 | BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; | ||
| 1021 | |||
| 1022 | // If doing layout and the package is in a container. | ||
| 1023 | if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer) | ||
| 1024 | { | ||
| 1025 | // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). | ||
| 1026 | hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory); | ||
| 1027 | ExitOnFailure(hr, "Failed to append layout container action."); | ||
| 1028 | } | ||
| 1029 | else | ||
| 1030 | { | ||
| 1031 | // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE). | ||
| 1032 | hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory); | ||
| 1033 | ExitOnFailure(hr, "Failed to append cache/layout payload action."); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); | ||
| 1037 | ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; | ||
| 1038 | pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | // Create package stop action. | ||
| 1042 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 1043 | ExitOnFailure(hr, "Failed to append cache action."); | ||
| 1044 | |||
| 1045 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; | ||
| 1046 | pCacheAction->packageStop.pPackage = pPackage; | ||
| 1047 | |||
| 1048 | // Update the start action with the location of the complete action. | ||
| 1049 | pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; | ||
| 1050 | |||
| 1051 | ++pPlan->cOverallProgressTicksTotal; | ||
| 1052 | |||
| 1053 | LExit: | ||
| 1054 | return hr; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | extern "C" HRESULT PlanCachePackage( | ||
| 1058 | __in BOOL fPerMachine, | ||
| 1059 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1060 | __in BURN_PLAN* pPlan, | ||
| 1061 | __in BURN_PACKAGE* pPackage, | ||
| 1062 | __in BURN_VARIABLES* pVariables, | ||
| 1063 | __out HANDLE* phSyncpointEvent | ||
| 1064 | ) | ||
| 1065 | { | ||
| 1066 | HRESULT hr = S_OK; | ||
| 1067 | BOOL fBARequestedCache = FALSE; | ||
| 1068 | |||
| 1069 | // Calculate the execute actions because we need them to decide whether the package should be cached. | ||
| 1070 | hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); | ||
| 1071 | ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId); | ||
| 1072 | |||
| 1073 | if (fBARequestedCache || NeedsCache(pPlan, pPackage)) | ||
| 1074 | { | ||
| 1075 | hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); | ||
| 1076 | ExitOnFailure(hr, "Failed to plan cache package."); | ||
| 1077 | |||
| 1078 | if (pPackage->fPerMachine) | ||
| 1079 | { | ||
| 1080 | pPlan->fPerMachine = TRUE; | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | // Make sure the package is properly ref-counted. | ||
| 1085 | hr = PlanDependencyActions(fPerMachine, pPlan, pPackage); | ||
| 1086 | ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId); | ||
| 1087 | |||
| 1088 | LExit: | ||
| 1089 | return hr; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | extern "C" HRESULT PlanExecutePackage( | ||
| 1093 | __in BOOL fPerMachine, | ||
| 1094 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 1095 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1096 | __in BURN_PLAN* pPlan, | ||
| 1097 | __in BURN_PACKAGE* pPackage, | ||
| 1098 | __in BURN_LOGGING* pLog, | ||
| 1099 | __in BURN_VARIABLES* pVariables, | ||
| 1100 | __inout HANDLE* phSyncpointEvent | ||
| 1101 | ) | ||
| 1102 | { | ||
| 1103 | HRESULT hr = S_OK; | ||
| 1104 | BOOL fBARequestedCache = FALSE; | ||
| 1105 | |||
| 1106 | hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache); | ||
| 1107 | ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId); | ||
| 1108 | |||
| 1109 | // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action. | ||
| 1110 | hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan); | ||
| 1111 | ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); | ||
| 1112 | |||
| 1113 | if (fBARequestedCache || NeedsCache(pPlan, pPackage)) | ||
| 1114 | { | ||
| 1115 | hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent); | ||
| 1116 | ExitOnFailure(hr, "Failed to plan cache package."); | ||
| 1117 | } | ||
| 1118 | else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache. | ||
| 1119 | (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)) | ||
| 1120 | ) | ||
| 1121 | { | ||
| 1122 | LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback)); | ||
| 1123 | pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | // Add the cache and install size to estimated size if it will be on the machine at the end of the install | ||
| 1127 | if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || | ||
| 1128 | (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) || | ||
| 1129 | BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType | ||
| 1130 | ) | ||
| 1131 | { | ||
| 1132 | // If the package will remain in the cache, add the package size to the estimated size | ||
| 1133 | if (BURN_CACHE_TYPE_YES <= pPackage->cacheType) | ||
| 1134 | { | ||
| 1135 | pPlan->qwEstimatedSize += pPackage->qwSize; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | // If the package will end up installed on the machine, add the install size to the estimated size. | ||
| 1139 | if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested) | ||
| 1140 | { | ||
| 1141 | // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well | ||
| 1142 | if (BURN_PACKAGE_TYPE_MSP == pPackage->type) | ||
| 1143 | { | ||
| 1144 | pPlan->qwEstimatedSize += pPackage->qwSize; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | pPlan->qwEstimatedSize += pPackage->qwInstallSize; | ||
| 1148 | } | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | // Add execute actions. | ||
| 1152 | switch (pPackage->type) | ||
| 1153 | { | ||
| 1154 | case BURN_PACKAGE_TYPE_EXE: | ||
| 1155 | hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); | ||
| 1156 | break; | ||
| 1157 | |||
| 1158 | case BURN_PACKAGE_TYPE_MSI: | ||
| 1159 | hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); | ||
| 1160 | break; | ||
| 1161 | |||
| 1162 | case BURN_PACKAGE_TYPE_MSP: | ||
| 1163 | hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); | ||
| 1164 | break; | ||
| 1165 | |||
| 1166 | case BURN_PACKAGE_TYPE_MSU: | ||
| 1167 | hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire); | ||
| 1168 | break; | ||
| 1169 | |||
| 1170 | default: | ||
| 1171 | hr = E_UNEXPECTED; | ||
| 1172 | ExitOnFailure(hr, "Invalid package type."); | ||
| 1173 | } | ||
| 1174 | ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId); | ||
| 1175 | |||
| 1176 | // Plan certain dependency actions after planning the package execute action. | ||
| 1177 | hr = DependencyPlanPackageComplete(pPackage, pPlan); | ||
| 1178 | ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); | ||
| 1179 | |||
| 1180 | // If we are going to take any action on this package, add progress for it. | ||
| 1181 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) | ||
| 1182 | { | ||
| 1183 | LoggingIncrementPackageSequence(); | ||
| 1184 | |||
| 1185 | ++pPlan->cExecutePackagesTotal; | ||
| 1186 | ++pPlan->cOverallProgressTicksTotal; | ||
| 1187 | |||
| 1188 | // If package is per-machine and is being executed, flag the plan to be per-machine as well. | ||
| 1189 | if (pPackage->fPerMachine) | ||
| 1190 | { | ||
| 1191 | pPlan->fPerMachine = TRUE; | ||
| 1192 | } | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | LExit: | ||
| 1196 | return hr; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | extern "C" HRESULT PlanRelatedBundlesBegin( | ||
| 1200 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1201 | __in BURN_REGISTRATION* pRegistration, | ||
| 1202 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 1203 | __in BURN_PLAN* pPlan | ||
| 1204 | ) | ||
| 1205 | { | ||
| 1206 | HRESULT hr = S_OK; | ||
| 1207 | LPWSTR* rgsczAncestors = NULL; | ||
| 1208 | UINT cAncestors = 0; | ||
| 1209 | STRINGDICT_HANDLE sdAncestors = NULL; | ||
| 1210 | |||
| 1211 | if (pRegistration->sczAncestors) | ||
| 1212 | { | ||
| 1213 | hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";"); | ||
| 1214 | ExitOnFailure(hr, "Failed to create string array from ancestors."); | ||
| 1215 | |||
| 1216 | hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE); | ||
| 1217 | ExitOnFailure(hr, "Failed to create dictionary from ancestors array."); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) | ||
| 1221 | { | ||
| 1222 | BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; | ||
| 1223 | pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1224 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1225 | |||
| 1226 | // Do not execute the same bundle twice. | ||
| 1227 | if (sdAncestors) | ||
| 1228 | { | ||
| 1229 | hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId); | ||
| 1230 | if (SUCCEEDED(hr)) | ||
| 1231 | { | ||
| 1232 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); | ||
| 1233 | continue; | ||
| 1234 | } | ||
| 1235 | else if (E_NOTFOUND != hr) | ||
| 1236 | { | ||
| 1237 | ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary."); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType) | ||
| 1241 | { | ||
| 1242 | // Avoid repair loops for older bundles that do not handle ancestors. | ||
| 1243 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType)); | ||
| 1244 | continue; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | // Pass along any ancestors and ourself to prevent infinite loops. | ||
| 1248 | if (pRegistration->sczAncestors && *pRegistration->sczAncestors) | ||
| 1249 | { | ||
| 1250 | hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId); | ||
| 1251 | ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors."); | ||
| 1252 | } | ||
| 1253 | else | ||
| 1254 | { | ||
| 1255 | hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0); | ||
| 1256 | ExitOnFailure(hr, "Failed to copy self to related bundle ancestors."); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | switch (pRelatedBundle->relationType) | ||
| 1260 | { | ||
| 1261 | case BOOTSTRAPPER_RELATION_UPGRADE: | ||
| 1262 | if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action) | ||
| 1263 | { | ||
| 1264 | pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1265 | } | ||
| 1266 | break; | ||
| 1267 | case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; | ||
| 1268 | case BOOTSTRAPPER_RELATION_ADDON: | ||
| 1269 | if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 1270 | { | ||
| 1271 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT; | ||
| 1272 | } | ||
| 1273 | else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action) | ||
| 1274 | { | ||
| 1275 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT; | ||
| 1276 | } | ||
| 1277 | else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) | ||
| 1278 | { | ||
| 1279 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; | ||
| 1280 | } | ||
| 1281 | break; | ||
| 1282 | case BOOTSTRAPPER_RELATION_DEPENDENT: | ||
| 1283 | // Automatically repair dependent bundles to restore missing | ||
| 1284 | // packages after uninstall unless we're being upgraded with the | ||
| 1285 | // assumption that upgrades are cumulative (as intended). | ||
| 1286 | if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 1287 | { | ||
| 1288 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR; | ||
| 1289 | } | ||
| 1290 | break; | ||
| 1291 | case BOOTSTRAPPER_RELATION_DETECT: | ||
| 1292 | break; | ||
| 1293 | default: | ||
| 1294 | hr = E_UNEXPECTED; | ||
| 1295 | ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType); | ||
| 1296 | break; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested; | ||
| 1300 | |||
| 1301 | hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested); | ||
| 1302 | ExitOnRootFailure(hr, "BA aborted plan related bundle."); | ||
| 1303 | |||
| 1304 | // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing. | ||
| 1305 | if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested) | ||
| 1306 | { | ||
| 1307 | LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested)); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting. | ||
| 1311 | if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) | ||
| 1312 | { | ||
| 1313 | if (0 < pRelatedBundle->package.cDependencyProviders) | ||
| 1314 | { | ||
| 1315 | // Bundles only support a single provider key. | ||
| 1316 | const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; | ||
| 1317 | |||
| 1318 | hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName); | ||
| 1319 | ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); | ||
| 1320 | } | ||
| 1321 | } | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | LExit: | ||
| 1325 | ReleaseDict(sdAncestors); | ||
| 1326 | ReleaseStrArray(rgsczAncestors, cAncestors); | ||
| 1327 | |||
| 1328 | return hr; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | extern "C" HRESULT PlanRelatedBundlesComplete( | ||
| 1332 | __in BURN_REGISTRATION* pRegistration, | ||
| 1333 | __in BURN_PLAN* pPlan, | ||
| 1334 | __in BURN_LOGGING* pLog, | ||
| 1335 | __in BURN_VARIABLES* pVariables, | ||
| 1336 | __inout HANDLE* phSyncpointEvent, | ||
| 1337 | __in DWORD dwExecuteActionEarlyIndex | ||
| 1338 | ) | ||
| 1339 | { | ||
| 1340 | HRESULT hr = S_OK; | ||
| 1341 | LPWSTR sczIgnoreDependencies = NULL; | ||
| 1342 | STRINGDICT_HANDLE sdProviderKeys = NULL; | ||
| 1343 | |||
| 1344 | // Get the list of dependencies to ignore to pass to related bundles. | ||
| 1345 | hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies); | ||
| 1346 | ExitOnFailure(hr, "Failed to get the list of dependencies to ignore."); | ||
| 1347 | |||
| 1348 | hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE); | ||
| 1349 | ExitOnFailure(hr, "Failed to create dictionary for planned packages."); | ||
| 1350 | |||
| 1351 | BOOL fExecutingAnyPackage = FALSE; | ||
| 1352 | |||
| 1353 | for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) | ||
| 1354 | { | ||
| 1355 | if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action) | ||
| 1356 | { | ||
| 1357 | fExecutingAnyPackage = TRUE; | ||
| 1358 | |||
| 1359 | BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage; | ||
| 1360 | if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) | ||
| 1361 | { | ||
| 1362 | if (0 < pPackage->cDependencyProviders) | ||
| 1363 | { | ||
| 1364 | // Bundles only support a single provider key. | ||
| 1365 | const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders; | ||
| 1366 | DictAddKey(sdProviderKeys, pProvider->sczKey); | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | } | ||
| 1370 | else | ||
| 1371 | { | ||
| 1372 | switch (pPlan->rgExecuteActions[i].type) | ||
| 1373 | { | ||
| 1374 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
| 1375 | fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action); | ||
| 1376 | break; | ||
| 1377 | |||
| 1378 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
| 1379 | fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action); | ||
| 1380 | break; | ||
| 1381 | |||
| 1382 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
| 1383 | fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action); | ||
| 1384 | break; | ||
| 1385 | } | ||
| 1386 | } | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) | ||
| 1390 | { | ||
| 1391 | DWORD *pdwInsertIndex = NULL; | ||
| 1392 | BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i; | ||
| 1393 | |||
| 1394 | // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same) | ||
| 1395 | if (0 < pRelatedBundle->package.cDependencyProviders) | ||
| 1396 | { | ||
| 1397 | // Bundles only support a single provider key. | ||
| 1398 | const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders; | ||
| 1399 | hr = DictKeyExists(sdProviderKeys, pProvider->sczKey); | ||
| 1400 | if (E_NOTFOUND != hr) | ||
| 1401 | { | ||
| 1402 | ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey); | ||
| 1403 | // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan | ||
| 1404 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey); | ||
| 1405 | continue; | ||
| 1406 | } | ||
| 1407 | else | ||
| 1408 | { | ||
| 1409 | hr = S_OK; | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | // For an uninstall, there is no need to repair dependent bundles if no packages are executing. | ||
| 1414 | if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 1415 | { | ||
| 1416 | pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1417 | LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType)); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) | ||
| 1421 | { | ||
| 1422 | // Addon and patch bundles will be passed a list of dependencies to ignore for planning. | ||
| 1423 | hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0); | ||
| 1424 | ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore."); | ||
| 1425 | |||
| 1426 | // Uninstall addons and patches early in the chain, before other packages are uninstalled. | ||
| 1427 | if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 1428 | { | ||
| 1429 | pdwInsertIndex = &dwExecuteActionEarlyIndex; | ||
| 1430 | } | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) | ||
| 1434 | { | ||
| 1435 | hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL); | ||
| 1436 | ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId); | ||
| 1437 | |||
| 1438 | // Calculate package states based on reference count for addon and patch related bundles. | ||
| 1439 | if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) | ||
| 1440 | { | ||
| 1441 | hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); | ||
| 1442 | ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId); | ||
| 1443 | |||
| 1444 | // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration. | ||
| 1445 | if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) | ||
| 1446 | { | ||
| 1447 | ++(*pdwInsertIndex); | ||
| 1448 | } | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE); | ||
| 1452 | ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId); | ||
| 1453 | |||
| 1454 | // Calculate package states based on reference count for addon and patch related bundles. | ||
| 1455 | if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) | ||
| 1456 | { | ||
| 1457 | hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); | ||
| 1458 | ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | // If we are going to take any action on this package, add progress for it. | ||
| 1462 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback) | ||
| 1463 | { | ||
| 1464 | LoggingIncrementPackageSequence(); | ||
| 1465 | |||
| 1466 | ++pPlan->cExecutePackagesTotal; | ||
| 1467 | ++pPlan->cOverallProgressTicksTotal; | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | // If package is per-machine and is being executed, flag the plan to be per-machine as well. | ||
| 1471 | if (pRelatedBundle->package.fPerMachine) | ||
| 1472 | { | ||
| 1473 | pPlan->fPerMachine = TRUE; | ||
| 1474 | } | ||
| 1475 | } | ||
| 1476 | else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType) | ||
| 1477 | { | ||
| 1478 | // Make sure the package is properly ref-counted even if no plan is requested. | ||
| 1479 | hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan); | ||
| 1480 | ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); | ||
| 1481 | |||
| 1482 | hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan); | ||
| 1483 | ExitOnFailure(hr, "Failed to plan related bundle package provider actions."); | ||
| 1484 | |||
| 1485 | hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); | ||
| 1486 | ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); | ||
| 1487 | } | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | LExit: | ||
| 1491 | ReleaseDict(sdProviderKeys); | ||
| 1492 | ReleaseStr(sczIgnoreDependencies); | ||
| 1493 | |||
| 1494 | return hr; | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | extern "C" HRESULT PlanFinalizeActions( | ||
| 1498 | __in BURN_PLAN* pPlan | ||
| 1499 | ) | ||
| 1500 | { | ||
| 1501 | HRESULT hr = S_OK; | ||
| 1502 | |||
| 1503 | hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); | ||
| 1504 | ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); | ||
| 1505 | |||
| 1506 | hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); | ||
| 1507 | ExitOnFailure(hr, "Failed to remove unnecessary execute actions."); | ||
| 1508 | |||
| 1509 | hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions); | ||
| 1510 | ExitOnFailure(hr, "Failed to finalize slipstream execute actions."); | ||
| 1511 | |||
| 1512 | hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); | ||
| 1513 | ExitOnFailure(hr, "Failed to finalize slipstream rollback actions."); | ||
| 1514 | |||
| 1515 | LExit: | ||
| 1516 | return hr; | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | extern "C" HRESULT PlanCleanPackage( | ||
| 1520 | __in BURN_PLAN* pPlan, | ||
| 1521 | __in BURN_PACKAGE* pPackage | ||
| 1522 | ) | ||
| 1523 | { | ||
| 1524 | HRESULT hr = S_OK; | ||
| 1525 | BOOL fPlanCleanPackage = FALSE; | ||
| 1526 | BURN_CLEAN_ACTION* pCleanAction = NULL; | ||
| 1527 | |||
| 1528 | // The following is a complex set of logic that determines when a package should be cleaned | ||
| 1529 | // from the cache. Start by noting that we only clean if the package is being acquired or | ||
| 1530 | // already cached and the package is not supposed to always be cached. | ||
| 1531 | if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) && | ||
| 1532 | (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action)) | ||
| 1533 | { | ||
| 1534 | // The following are all different reasons why the package should be cleaned from the cache. | ||
| 1535 | // The else-ifs are used to make the conditions easier to see (rather than have them combined | ||
| 1536 | // in one huge condition). | ||
| 1537 | if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached. | ||
| 1538 | { | ||
| 1539 | fPlanCleanPackage = TRUE; | ||
| 1540 | } | ||
| 1541 | else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || | ||
| 1542 | BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and | ||
| 1543 | BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed. | ||
| 1544 | { | ||
| 1545 | fPlanCleanPackage = TRUE; | ||
| 1546 | } | ||
| 1547 | else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested || | ||
| 1548 | BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but | ||
| 1549 | BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and | ||
| 1550 | !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and | ||
| 1551 | BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. | ||
| 1552 | { | ||
| 1553 | fPlanCleanPackage = TRUE; | ||
| 1554 | } | ||
| 1555 | else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and | ||
| 1556 | BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and | ||
| 1557 | BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and | ||
| 1558 | !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and | ||
| 1559 | BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed. | ||
| 1560 | { | ||
| 1561 | fPlanCleanPackage = TRUE; | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | if (fPlanCleanPackage) | ||
| 1566 | { | ||
| 1567 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5); | ||
| 1568 | ExitOnFailure(hr, "Failed to grow plan's array of clean actions."); | ||
| 1569 | |||
| 1570 | pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions; | ||
| 1571 | ++pPlan->cCleanActions; | ||
| 1572 | |||
| 1573 | pCleanAction->pPackage = pPackage; | ||
| 1574 | |||
| 1575 | pPackage->fUncache = TRUE; | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | LExit: | ||
| 1579 | return hr; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | extern "C" HRESULT PlanExecuteCacheSyncAndRollback( | ||
| 1583 | __in BURN_PLAN* pPlan, | ||
| 1584 | __in BURN_PACKAGE* pPackage, | ||
| 1585 | __in HANDLE hCacheEvent, | ||
| 1586 | __in BOOL fPlanPackageCacheRollback | ||
| 1587 | ) | ||
| 1588 | { | ||
| 1589 | HRESULT hr = S_OK; | ||
| 1590 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 1591 | |||
| 1592 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 1593 | ExitOnFailure(hr, "Failed to append wait action for caching."); | ||
| 1594 | |||
| 1595 | pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT; | ||
| 1596 | pAction->syncpoint.hEvent = hCacheEvent; | ||
| 1597 | |||
| 1598 | if (fPlanPackageCacheRollback) | ||
| 1599 | { | ||
| 1600 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 1601 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 1602 | |||
| 1603 | pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE; | ||
| 1604 | pAction->uncachePackage.pPackage = pPackage; | ||
| 1605 | |||
| 1606 | hr = PlanExecuteCheckpoint(pPlan); | ||
| 1607 | ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback."); | ||
| 1608 | } | ||
| 1609 | |||
| 1610 | LExit: | ||
| 1611 | return hr; | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | extern "C" HRESULT PlanExecuteCheckpoint( | ||
| 1615 | __in BURN_PLAN* pPlan | ||
| 1616 | ) | ||
| 1617 | { | ||
| 1618 | HRESULT hr = S_OK; | ||
| 1619 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 1620 | DWORD dwCheckpointId = GetNextCheckpointId(); | ||
| 1621 | |||
| 1622 | // execute checkpoint | ||
| 1623 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
| 1624 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 1625 | |||
| 1626 | pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; | ||
| 1627 | pAction->checkpoint.dwId = dwCheckpointId; | ||
| 1628 | |||
| 1629 | // rollback checkpoint | ||
| 1630 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
| 1631 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 1632 | |||
| 1633 | pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; | ||
| 1634 | pAction->checkpoint.dwId = dwCheckpointId; | ||
| 1635 | |||
| 1636 | LExit: | ||
| 1637 | return hr; | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | extern "C" HRESULT PlanInsertExecuteAction( | ||
| 1641 | __in DWORD dwIndex, | ||
| 1642 | __in BURN_PLAN* pPlan, | ||
| 1643 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
| 1644 | ) | ||
| 1645 | { | ||
| 1646 | HRESULT hr = S_OK; | ||
| 1647 | |||
| 1648 | hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); | ||
| 1649 | ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); | ||
| 1650 | |||
| 1651 | *ppExecuteAction = pPlan->rgExecuteActions + dwIndex; | ||
| 1652 | ++pPlan->cExecuteActions; | ||
| 1653 | |||
| 1654 | LExit: | ||
| 1655 | return hr; | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | extern "C" HRESULT PlanInsertRollbackAction( | ||
| 1659 | __in DWORD dwIndex, | ||
| 1660 | __in BURN_PLAN* pPlan, | ||
| 1661 | __out BURN_EXECUTE_ACTION** ppRollbackAction | ||
| 1662 | ) | ||
| 1663 | { | ||
| 1664 | HRESULT hr = S_OK; | ||
| 1665 | |||
| 1666 | hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); | ||
| 1667 | ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); | ||
| 1668 | |||
| 1669 | *ppRollbackAction = pPlan->rgRollbackActions + dwIndex; | ||
| 1670 | ++pPlan->cRollbackActions; | ||
| 1671 | |||
| 1672 | LExit: | ||
| 1673 | return hr; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | extern "C" HRESULT PlanAppendExecuteAction( | ||
| 1677 | __in BURN_PLAN* pPlan, | ||
| 1678 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
| 1679 | ) | ||
| 1680 | { | ||
| 1681 | HRESULT hr = S_OK; | ||
| 1682 | |||
| 1683 | hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); | ||
| 1684 | ExitOnFailure(hr, "Failed to grow plan's array of execute actions."); | ||
| 1685 | |||
| 1686 | *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions; | ||
| 1687 | ++pPlan->cExecuteActions; | ||
| 1688 | |||
| 1689 | LExit: | ||
| 1690 | return hr; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | extern "C" HRESULT PlanAppendRollbackAction( | ||
| 1694 | __in BURN_PLAN* pPlan, | ||
| 1695 | __out BURN_EXECUTE_ACTION** ppRollbackAction | ||
| 1696 | ) | ||
| 1697 | { | ||
| 1698 | HRESULT hr = S_OK; | ||
| 1699 | |||
| 1700 | hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5); | ||
| 1701 | ExitOnFailure(hr, "Failed to grow plan's array of rollback actions."); | ||
| 1702 | |||
| 1703 | *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions; | ||
| 1704 | ++pPlan->cRollbackActions; | ||
| 1705 | |||
| 1706 | LExit: | ||
| 1707 | return hr; | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | extern "C" HRESULT PlanKeepRegistration( | ||
| 1711 | __in BURN_PLAN* pPlan, | ||
| 1712 | __in DWORD iAfterExecutePackageAction, | ||
| 1713 | __in DWORD iBeforeRollbackPackageAction | ||
| 1714 | ) | ||
| 1715 | { | ||
| 1716 | HRESULT hr = S_OK; | ||
| 1717 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 1718 | |||
| 1719 | if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) | ||
| 1720 | { | ||
| 1721 | hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); | ||
| 1722 | ExitOnFailure(hr, "Failed to insert keep registration execute action."); | ||
| 1723 | |||
| 1724 | pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; | ||
| 1725 | pAction->registration.fKeep = TRUE; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction) | ||
| 1729 | { | ||
| 1730 | hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction); | ||
| 1731 | ExitOnFailure(hr, "Failed to insert keep registration rollback action."); | ||
| 1732 | |||
| 1733 | pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; | ||
| 1734 | pAction->registration.fKeep = FALSE; | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | LExit: | ||
| 1738 | return hr; | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | extern "C" HRESULT PlanRemoveRegistration( | ||
| 1742 | __in BURN_PLAN* pPlan, | ||
| 1743 | __in DWORD iAfterExecutePackageAction, | ||
| 1744 | __in DWORD iAfterRollbackPackageAction | ||
| 1745 | ) | ||
| 1746 | { | ||
| 1747 | HRESULT hr = S_OK; | ||
| 1748 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
| 1749 | |||
| 1750 | if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction) | ||
| 1751 | { | ||
| 1752 | hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction); | ||
| 1753 | ExitOnFailure(hr, "Failed to insert remove registration execute action."); | ||
| 1754 | |||
| 1755 | pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; | ||
| 1756 | pAction->registration.fKeep = FALSE; | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction) | ||
| 1760 | { | ||
| 1761 | hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction); | ||
| 1762 | ExitOnFailure(hr, "Failed to insert remove registration rollback action."); | ||
| 1763 | |||
| 1764 | pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION; | ||
| 1765 | pAction->registration.fKeep = TRUE; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | LExit: | ||
| 1769 | return hr; | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | extern "C" HRESULT PlanRollbackBoundaryBegin( | ||
| 1773 | __in BURN_PLAN* pPlan, | ||
| 1774 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
| 1775 | ) | ||
| 1776 | { | ||
| 1777 | HRESULT hr = S_OK; | ||
| 1778 | BURN_EXECUTE_ACTION* pExecuteAction = NULL; | ||
| 1779 | |||
| 1780 | // Add begin rollback boundary to execute plan. | ||
| 1781 | hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); | ||
| 1782 | ExitOnFailure(hr, "Failed to append rollback boundary begin action."); | ||
| 1783 | |||
| 1784 | pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; | ||
| 1785 | pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; | ||
| 1786 | |||
| 1787 | // Add begin rollback boundary to rollback plan. | ||
| 1788 | hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); | ||
| 1789 | ExitOnFailure(hr, "Failed to append rollback boundary begin action."); | ||
| 1790 | |||
| 1791 | pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY; | ||
| 1792 | pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary; | ||
| 1793 | |||
| 1794 | LExit: | ||
| 1795 | return hr; | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | extern "C" HRESULT PlanRollbackBoundaryComplete( | ||
| 1799 | __in BURN_PLAN* pPlan | ||
| 1800 | ) | ||
| 1801 | { | ||
| 1802 | HRESULT hr = S_OK; | ||
| 1803 | BURN_EXECUTE_ACTION* pExecuteAction = NULL; | ||
| 1804 | DWORD dwCheckpointId = 0; | ||
| 1805 | |||
| 1806 | // Add checkpoints. | ||
| 1807 | dwCheckpointId = GetNextCheckpointId(); | ||
| 1808 | |||
| 1809 | hr = PlanAppendExecuteAction(pPlan, &pExecuteAction); | ||
| 1810 | ExitOnFailure(hr, "Failed to append execute action."); | ||
| 1811 | |||
| 1812 | pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; | ||
| 1813 | pExecuteAction->checkpoint.dwId = dwCheckpointId; | ||
| 1814 | |||
| 1815 | hr = PlanAppendRollbackAction(pPlan, &pExecuteAction); | ||
| 1816 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
| 1817 | |||
| 1818 | pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT; | ||
| 1819 | pExecuteAction->checkpoint.dwId = dwCheckpointId; | ||
| 1820 | |||
| 1821 | LExit: | ||
| 1822 | return hr; | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | /******************************************************************* | ||
| 1826 | PlanSetResumeCommand - Initializes resume command string | ||
| 1827 | |||
| 1828 | *******************************************************************/ | ||
| 1829 | extern "C" HRESULT PlanSetResumeCommand( | ||
| 1830 | __in BURN_REGISTRATION* pRegistration, | ||
| 1831 | __in BOOTSTRAPPER_ACTION action, | ||
| 1832 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 1833 | __in BURN_LOGGING* pLog | ||
| 1834 | ) | ||
| 1835 | { | ||
| 1836 | HRESULT hr = S_OK; | ||
| 1837 | |||
| 1838 | // build the resume command-line. | ||
| 1839 | hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine); | ||
| 1840 | ExitOnFailure(hr, "Failed to recreate resume command-line."); | ||
| 1841 | |||
| 1842 | LExit: | ||
| 1843 | return hr; | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | |||
| 1847 | // internal function definitions | ||
| 1848 | |||
| 1849 | static void UninitializeRegistrationAction( | ||
| 1850 | __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
| 1851 | ) | ||
| 1852 | { | ||
| 1853 | ReleaseStr(pAction->sczDependentProviderKey); | ||
| 1854 | ReleaseStr(pAction->sczBundleId); | ||
| 1855 | memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION)); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | static void UninitializeCacheAction( | ||
| 1859 | __in BURN_CACHE_ACTION* pCacheAction | ||
| 1860 | ) | ||
| 1861 | { | ||
| 1862 | switch (pCacheAction->type) | ||
| 1863 | { | ||
| 1864 | case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: | ||
| 1865 | ReleaseHandle(pCacheAction->syncpoint.hEvent); | ||
| 1866 | break; | ||
| 1867 | |||
| 1868 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
| 1869 | ReleaseStr(pCacheAction->bundleLayout.sczExecutableName); | ||
| 1870 | ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory); | ||
| 1871 | ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath); | ||
| 1872 | break; | ||
| 1873 | |||
| 1874 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
| 1875 | ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath); | ||
| 1876 | break; | ||
| 1877 | |||
| 1878 | case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: | ||
| 1879 | ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath); | ||
| 1880 | ReleaseMem(pCacheAction->extractContainer.rgPayloads); | ||
| 1881 | break; | ||
| 1882 | |||
| 1883 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
| 1884 | ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath); | ||
| 1885 | break; | ||
| 1886 | |||
| 1887 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
| 1888 | ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath); | ||
| 1889 | break; | ||
| 1890 | } | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | static void ResetPlannedPackageState( | ||
| 1894 | __in BURN_PACKAGE* pPackage | ||
| 1895 | ) | ||
| 1896 | { | ||
| 1897 | // Reset package state that is a result of planning. | ||
| 1898 | pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; | ||
| 1899 | pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1900 | pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1901 | pPackage->fAcquire = FALSE; | ||
| 1902 | pPackage->fUncache = FALSE; | ||
| 1903 | pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 1904 | pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 1905 | pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE; | ||
| 1906 | pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE; | ||
| 1907 | pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; | ||
| 1908 | pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; | ||
| 1909 | pPackage->fDependencyManagerWasHere = FALSE; | ||
| 1910 | |||
| 1911 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures) | ||
| 1912 | { | ||
| 1913 | for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) | ||
| 1914 | { | ||
| 1915 | BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; | ||
| 1916 | |||
| 1917 | pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE; | ||
| 1918 | pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE; | ||
| 1919 | } | ||
| 1920 | } | ||
| 1921 | else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts) | ||
| 1922 | { | ||
| 1923 | for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) | ||
| 1924 | { | ||
| 1925 | BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i]; | ||
| 1926 | |||
| 1927 | pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 1928 | pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
| 1929 | } | ||
| 1930 | } | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | static HRESULT GetActionDefaultRequestState( | ||
| 1934 | __in BOOTSTRAPPER_ACTION action, | ||
| 1935 | __in BOOL fPermanent, | ||
| 1936 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
| 1937 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
| 1938 | ) | ||
| 1939 | { | ||
| 1940 | HRESULT hr = S_OK; | ||
| 1941 | |||
| 1942 | switch (action) | ||
| 1943 | { | ||
| 1944 | case BOOTSTRAPPER_ACTION_CACHE: | ||
| 1945 | switch (currentState) | ||
| 1946 | { | ||
| 1947 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 1948 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; | ||
| 1949 | break; | ||
| 1950 | |||
| 1951 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 1952 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1953 | break; | ||
| 1954 | |||
| 1955 | default: | ||
| 1956 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; | ||
| 1957 | break; | ||
| 1958 | } | ||
| 1959 | break; | ||
| 1960 | |||
| 1961 | case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough; | ||
| 1962 | case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough; | ||
| 1963 | case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED: | ||
| 1964 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; | ||
| 1965 | break; | ||
| 1966 | |||
| 1967 | case BOOTSTRAPPER_ACTION_REPAIR: | ||
| 1968 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; | ||
| 1969 | break; | ||
| 1970 | |||
| 1971 | case BOOTSTRAPPER_ACTION_UNINSTALL: | ||
| 1972 | *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT; | ||
| 1973 | break; | ||
| 1974 | |||
| 1975 | case BOOTSTRAPPER_ACTION_MODIFY: | ||
| 1976 | switch (currentState) | ||
| 1977 | { | ||
| 1978 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
| 1979 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; | ||
| 1980 | break; | ||
| 1981 | |||
| 1982 | case BOOTSTRAPPER_PACKAGE_STATE_CACHED: | ||
| 1983 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE; | ||
| 1984 | break; | ||
| 1985 | |||
| 1986 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
| 1987 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; | ||
| 1988 | break; | ||
| 1989 | |||
| 1990 | default: | ||
| 1991 | *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; | ||
| 1992 | break; | ||
| 1993 | } | ||
| 1994 | break; | ||
| 1995 | |||
| 1996 | default: | ||
| 1997 | hr = E_INVALIDARG; | ||
| 1998 | ExitOnRootFailure(hr, "Invalid action state."); | ||
| 1999 | } | ||
| 2000 | |||
| 2001 | LExit: | ||
| 2002 | return hr; | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | static HRESULT AddRegistrationAction( | ||
| 2006 | __in BURN_PLAN* pPlan, | ||
| 2007 | __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type, | ||
| 2008 | __in_z LPCWSTR wzDependentProviderKey, | ||
| 2009 | __in_z LPCWSTR wzOwnerBundleId | ||
| 2010 | ) | ||
| 2011 | { | ||
| 2012 | HRESULT hr = S_OK; | ||
| 2013 | BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER; | ||
| 2014 | BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL; | ||
| 2015 | |||
| 2016 | // Create forward registration action. | ||
| 2017 | hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); | ||
| 2018 | ExitOnFailure(hr, "Failed to grow plan's array of registration actions."); | ||
| 2019 | |||
| 2020 | pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions; | ||
| 2021 | ++pPlan->cRegistrationActions; | ||
| 2022 | |||
| 2023 | pAction->type = type; | ||
| 2024 | |||
| 2025 | hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); | ||
| 2026 | ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); | ||
| 2027 | |||
| 2028 | hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); | ||
| 2029 | ExitOnFailure(hr, "Failed to copy dependent provider key to registration action."); | ||
| 2030 | |||
| 2031 | // Create rollback registration action. | ||
| 2032 | hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5); | ||
| 2033 | ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions."); | ||
| 2034 | |||
| 2035 | pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions; | ||
| 2036 | ++pPlan->cRollbackRegistrationActions; | ||
| 2037 | |||
| 2038 | pAction->type = rollbackType; | ||
| 2039 | |||
| 2040 | hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0); | ||
| 2041 | ExitOnFailure(hr, "Failed to copy owner bundle to registration action."); | ||
| 2042 | |||
| 2043 | hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0); | ||
| 2044 | ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action."); | ||
| 2045 | |||
| 2046 | LExit: | ||
| 2047 | return hr; | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | static HRESULT AddCachePackage( | ||
| 2051 | __in BURN_PLAN* pPlan, | ||
| 2052 | __in BURN_PACKAGE* pPackage, | ||
| 2053 | __out HANDLE* phSyncpointEvent | ||
| 2054 | ) | ||
| 2055 | { | ||
| 2056 | HRESULT hr = S_OK; | ||
| 2057 | |||
| 2058 | // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first. | ||
| 2059 | if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages) | ||
| 2060 | { | ||
| 2061 | hr = AddCacheSlipstreamMsps(pPlan, pPackage); | ||
| 2062 | ExitOnFailure(hr, "Failed to plan slipstream patches for package."); | ||
| 2063 | } | ||
| 2064 | |||
| 2065 | hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent); | ||
| 2066 | ExitOnFailure(hr, "Failed to plan cache package."); | ||
| 2067 | |||
| 2068 | LExit: | ||
| 2069 | return hr; | ||
| 2070 | } | ||
| 2071 | |||
| 2072 | static HRESULT AddCachePackageHelper( | ||
| 2073 | __in BURN_PLAN* pPlan, | ||
| 2074 | __in BURN_PACKAGE* pPackage, | ||
| 2075 | __out HANDLE* phSyncpointEvent | ||
| 2076 | ) | ||
| 2077 | { | ||
| 2078 | AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id."); | ||
| 2079 | |||
| 2080 | HRESULT hr = S_OK; | ||
| 2081 | BURN_CACHE_ACTION* pCacheAction = NULL; | ||
| 2082 | DWORD dwCheckpoint = 0; | ||
| 2083 | DWORD iPackageStartAction = 0; | ||
| 2084 | |||
| 2085 | BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent); | ||
| 2086 | if (fPlanned) | ||
| 2087 | { | ||
| 2088 | ExitFunction(); | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | // Cache checkpoints happen before the package is cached because downloading packages' | ||
| 2092 | // payloads will not roll themselves back the way installation packages rollback on | ||
| 2093 | // failure automatically. | ||
| 2094 | dwCheckpoint = GetNextCheckpointId(); | ||
| 2095 | |||
| 2096 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2097 | ExitOnFailure(hr, "Failed to append package start action."); | ||
| 2098 | |||
| 2099 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; | ||
| 2100 | pCacheAction->checkpoint.dwId = dwCheckpoint; | ||
| 2101 | |||
| 2102 | // Only plan the cache rollback if the package is also going to be uninstalled; | ||
| 2103 | // otherwise, future operations like repair will not be able to locate the cached package. | ||
| 2104 | BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback); | ||
| 2105 | |||
| 2106 | if (fPlanCacheRollback) | ||
| 2107 | { | ||
| 2108 | hr = AppendRollbackCacheAction(pPlan, &pCacheAction); | ||
| 2109 | ExitOnFailure(hr, "Failed to append rollback cache action."); | ||
| 2110 | |||
| 2111 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT; | ||
| 2112 | pCacheAction->checkpoint.dwId = dwCheckpoint; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | // Plan the package start. | ||
| 2116 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2117 | ExitOnFailure(hr, "Failed to append package start action."); | ||
| 2118 | |||
| 2119 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START; | ||
| 2120 | pCacheAction->packageStart.pPackage = pPackage; | ||
| 2121 | |||
| 2122 | // Remember the index for the package start action (which is now the last in the cache | ||
| 2123 | // actions array) because we have to update this action after processing all the payloads | ||
| 2124 | // and the array may be resized later which would move a pointer around in memory. | ||
| 2125 | iPackageStartAction = pPlan->cCacheActions - 1; | ||
| 2126 | |||
| 2127 | if (fPlanCacheRollback) | ||
| 2128 | { | ||
| 2129 | // Create a package cache rollback action. | ||
| 2130 | hr = AppendRollbackCacheAction(pPlan, &pCacheAction); | ||
| 2131 | ExitOnFailure(hr, "Failed to append rollback cache action."); | ||
| 2132 | |||
| 2133 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE; | ||
| 2134 | pCacheAction->rollbackPackage.pPackage = pPackage; | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | // Add all the payload cache operations to the plan for this package. | ||
| 2138 | for (DWORD i = 0; i < pPackage->cPayloads; ++i) | ||
| 2139 | { | ||
| 2140 | BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i]; | ||
| 2141 | |||
| 2142 | hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL); | ||
| 2143 | ExitOnFailure(hr, "Failed to append payload cache action."); | ||
| 2144 | |||
| 2145 | Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type); | ||
| 2146 | ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads; | ||
| 2147 | pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize; | ||
| 2148 | } | ||
| 2149 | |||
| 2150 | // Create package stop action. | ||
| 2151 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2152 | ExitOnFailure(hr, "Failed to append cache action."); | ||
| 2153 | |||
| 2154 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP; | ||
| 2155 | pCacheAction->packageStop.pPackage = pPackage; | ||
| 2156 | |||
| 2157 | // Update the start action with the location of the complete action. | ||
| 2158 | pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1; | ||
| 2159 | |||
| 2160 | // Create syncpoint action. | ||
| 2161 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2162 | ExitOnFailure(hr, "Failed to append cache action."); | ||
| 2163 | |||
| 2164 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT; | ||
| 2165 | pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 2166 | ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event."); | ||
| 2167 | |||
| 2168 | *phSyncpointEvent = pCacheAction->syncpoint.hEvent; | ||
| 2169 | |||
| 2170 | ++pPlan->cOverallProgressTicksTotal; | ||
| 2171 | |||
| 2172 | // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only | ||
| 2173 | // did cache operations to verify the cache is valid so we did not plan the acquisition of the package. | ||
| 2174 | pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache); | ||
| 2175 | |||
| 2176 | LExit: | ||
| 2177 | return hr; | ||
| 2178 | } | ||
| 2179 | |||
| 2180 | static HRESULT AddCacheSlipstreamMsps( | ||
| 2181 | __in BURN_PLAN* pPlan, | ||
| 2182 | __in BURN_PACKAGE* pPackage | ||
| 2183 | ) | ||
| 2184 | { | ||
| 2185 | HRESULT hr = S_OK; | ||
| 2186 | HANDLE hIgnored = NULL; | ||
| 2187 | |||
| 2188 | AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches."); | ||
| 2189 | |||
| 2190 | for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) | ||
| 2191 | { | ||
| 2192 | BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i]; | ||
| 2193 | AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); | ||
| 2194 | |||
| 2195 | hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored); | ||
| 2196 | ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId); | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | LExit: | ||
| 2200 | return hr; | ||
| 2201 | } | ||
| 2202 | |||
| 2203 | static BOOL AlreadyPlannedCachePackage( | ||
| 2204 | __in BURN_PLAN* pPlan, | ||
| 2205 | __in_z LPCWSTR wzPackageId, | ||
| 2206 | __out HANDLE* phSyncpointEvent | ||
| 2207 | ) | ||
| 2208 | { | ||
| 2209 | BOOL fPlanned = FALSE; | ||
| 2210 | |||
| 2211 | for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction) | ||
| 2212 | { | ||
| 2213 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction; | ||
| 2214 | |||
| 2215 | if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type) | ||
| 2216 | { | ||
| 2217 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1)) | ||
| 2218 | { | ||
| 2219 | if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type) | ||
| 2220 | { | ||
| 2221 | *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | fPlanned = TRUE; | ||
| 2225 | break; | ||
| 2226 | } | ||
| 2227 | } | ||
| 2228 | } | ||
| 2229 | |||
| 2230 | return fPlanned; | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | static DWORD GetNextCheckpointId() | ||
| 2234 | { | ||
| 2235 | static DWORD dwCounter = 0; | ||
| 2236 | return ++dwCounter; | ||
| 2237 | } | ||
| 2238 | |||
| 2239 | static HRESULT AppendCacheAction( | ||
| 2240 | __in BURN_PLAN* pPlan, | ||
| 2241 | __out BURN_CACHE_ACTION** ppCacheAction | ||
| 2242 | ) | ||
| 2243 | { | ||
| 2244 | HRESULT hr = S_OK; | ||
| 2245 | |||
| 2246 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); | ||
| 2247 | ExitOnFailure(hr, "Failed to grow plan's array of cache actions."); | ||
| 2248 | |||
| 2249 | *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions; | ||
| 2250 | ++pPlan->cCacheActions; | ||
| 2251 | |||
| 2252 | LExit: | ||
| 2253 | return hr; | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | static HRESULT AppendRollbackCacheAction( | ||
| 2257 | __in BURN_PLAN* pPlan, | ||
| 2258 | __out BURN_CACHE_ACTION** ppCacheAction | ||
| 2259 | ) | ||
| 2260 | { | ||
| 2261 | HRESULT hr = S_OK; | ||
| 2262 | |||
| 2263 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5); | ||
| 2264 | ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions."); | ||
| 2265 | |||
| 2266 | *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions; | ||
| 2267 | ++pPlan->cRollbackCacheActions; | ||
| 2268 | |||
| 2269 | LExit: | ||
| 2270 | return hr; | ||
| 2271 | } | ||
| 2272 | |||
| 2273 | static HRESULT AppendLayoutContainerAction( | ||
| 2274 | __in BURN_PLAN* pPlan, | ||
| 2275 | __in_opt BURN_PACKAGE* pPackage, | ||
| 2276 | __in DWORD iPackageStartAction, | ||
| 2277 | __in BURN_CONTAINER* pContainer, | ||
| 2278 | __in BOOL fContainerCached, | ||
| 2279 | __in_z LPCWSTR wzLayoutDirectory | ||
| 2280 | ) | ||
| 2281 | { | ||
| 2282 | HRESULT hr = S_OK; | ||
| 2283 | BURN_CACHE_ACTION* pAcquireAction = NULL; | ||
| 2284 | DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2285 | LPWSTR sczContainerWorkingPath = NULL; | ||
| 2286 | BURN_CACHE_ACTION* pCacheAction = NULL; | ||
| 2287 | BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; | ||
| 2288 | |||
| 2289 | // No need to do anything if the container is already cached or is attached to the bundle (since the | ||
| 2290 | // bundle itself will already have a layout action). | ||
| 2291 | if (fContainerCached || pContainer->fAttached) | ||
| 2292 | { | ||
| 2293 | ExitFunction(); | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so | ||
| 2297 | // don't do it again. Otherwise, plan away! | ||
| 2298 | if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL)) | ||
| 2299 | { | ||
| 2300 | hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction); | ||
| 2301 | ExitOnFailure(hr, "Failed to append acquire container action for layout to plan."); | ||
| 2302 | |||
| 2303 | Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type); | ||
| 2304 | |||
| 2305 | // Create the layout container action. | ||
| 2306 | hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0); | ||
| 2307 | ExitOnFailure(hr, "Failed to copy container working path for layout."); | ||
| 2308 | |||
| 2309 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2310 | ExitOnFailure(hr, "Failed to append cache action to cache payload."); | ||
| 2311 | |||
| 2312 | hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); | ||
| 2313 | ExitOnFailure(hr, "Failed to create container progress."); | ||
| 2314 | |||
| 2315 | hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0); | ||
| 2316 | ExitOnFailure(hr, "Failed to copy layout directory into plan."); | ||
| 2317 | |||
| 2318 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER; | ||
| 2319 | pCacheAction->layoutContainer.pPackage = pPackage; | ||
| 2320 | pCacheAction->layoutContainer.pContainer = pContainer; | ||
| 2321 | pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex; | ||
| 2322 | pCacheAction->layoutContainer.fMove = TRUE; | ||
| 2323 | pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction; | ||
| 2324 | pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath; | ||
| 2325 | sczContainerWorkingPath = NULL; | ||
| 2326 | } | ||
| 2327 | |||
| 2328 | LExit: | ||
| 2329 | ReleaseNullStr(sczContainerWorkingPath); | ||
| 2330 | |||
| 2331 | return hr; | ||
| 2332 | } | ||
| 2333 | |||
| 2334 | static HRESULT AppendCacheOrLayoutPayloadAction( | ||
| 2335 | __in BURN_PLAN* pPlan, | ||
| 2336 | __in_opt BURN_PACKAGE* pPackage, | ||
| 2337 | __in DWORD iPackageStartAction, | ||
| 2338 | __in BURN_PAYLOAD* pPayload, | ||
| 2339 | __in BOOL fPayloadCached, | ||
| 2340 | __in_z_opt LPCWSTR wzLayoutDirectory | ||
| 2341 | ) | ||
| 2342 | { | ||
| 2343 | HRESULT hr = S_OK; | ||
| 2344 | LPWSTR sczPayloadWorkingPath = NULL; | ||
| 2345 | BURN_CACHE_ACTION* pCacheAction = NULL; | ||
| 2346 | DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2347 | BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; | ||
| 2348 | |||
| 2349 | hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath); | ||
| 2350 | ExitOnFailure(hr, "Failed to calculate unverified path for payload."); | ||
| 2351 | |||
| 2352 | // If the payload is in a container, ensure the container is being acquired | ||
| 2353 | // then add this payload to the list of payloads to extract already in the plan. | ||
| 2354 | if (pPayload->pContainer) | ||
| 2355 | { | ||
| 2356 | BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL; | ||
| 2357 | BURN_CACHE_ACTION* pThisPackageExtractAction = NULL; | ||
| 2358 | |||
| 2359 | // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting | ||
| 2360 | // all the needed payloads from the container in a single pass is the most efficient way to extract files from | ||
| 2361 | // containers. If there is not an extract container action before our package, that is okay because we'll create | ||
| 2362 | // an extract container action for our package in a second anyway. | ||
| 2363 | if (!fPayloadCached) | ||
| 2364 | { | ||
| 2365 | if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL)) | ||
| 2366 | { | ||
| 2367 | hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); | ||
| 2368 | ExitOnFailure(hr, "Failed to add extract payload action to previous package."); | ||
| 2369 | } | ||
| 2370 | } | ||
| 2371 | |||
| 2372 | // If there is already an extract container action after our package start action then try to find an acquire action | ||
| 2373 | // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing | ||
| 2374 | // extract action as the "try again" action. | ||
| 2375 | if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction)) | ||
| 2376 | { | ||
| 2377 | DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2378 | if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction)) | ||
| 2379 | { | ||
| 2380 | iTryAgainAction = iAcquireAction; | ||
| 2381 | } | ||
| 2382 | } | ||
| 2383 | else // did not find an extract container action for our package. | ||
| 2384 | { | ||
| 2385 | // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The | ||
| 2386 | // acquire and extract action will be skipped if the payload is already cached or was added to a previous | ||
| 2387 | // package's extract action above. | ||
| 2388 | // | ||
| 2389 | // These actions always exist (even when they are likely to be skipped) so that "try again" will not | ||
| 2390 | // jump so far back in the plan that you end up extracting payloads for other packages. With these actions | ||
| 2391 | // "try again" will only retry the extraction for payloads in this package. | ||
| 2392 | hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction); | ||
| 2393 | ExitOnFailure(hr, "Failed to create container extract action."); | ||
| 2394 | } | ||
| 2395 | ExitOnFailure(hr, "Failed while searching for package's container extract action."); | ||
| 2396 | |||
| 2397 | // We *always* add the payload to this package's extract action even though the extract action | ||
| 2398 | // is probably being skipped until retry if there was a previous package extract action. | ||
| 2399 | hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath); | ||
| 2400 | ExitOnFailure(hr, "Failed to add extract payload to current package."); | ||
| 2401 | } | ||
| 2402 | else // add a payload acquire action to the plan. | ||
| 2403 | { | ||
| 2404 | // Try to find an existing acquire action for this payload. If one is not found, | ||
| 2405 | // we'll create it. At the same time we will change any cache/layout payload actions | ||
| 2406 | // that would "MOVE" the file to "COPY" so that our new cache/layout action below | ||
| 2407 | // can do the move. | ||
| 2408 | pCacheAction = ProcessSharedPayload(pPlan, pPayload); | ||
| 2409 | if (!pCacheAction) | ||
| 2410 | { | ||
| 2411 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2412 | ExitOnFailure(hr, "Failed to append cache action to acquire payload."); | ||
| 2413 | |||
| 2414 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD; | ||
| 2415 | pCacheAction->fSkipUntilRetried = fPayloadCached; | ||
| 2416 | pCacheAction->resolvePayload.pPackage = pPackage; | ||
| 2417 | pCacheAction->resolvePayload.pPayload = pPayload; | ||
| 2418 | hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0); | ||
| 2419 | ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire."); | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | iTryAgainAction = static_cast<DWORD>(pCacheAction - pPlan->rgCacheActions); | ||
| 2423 | pCacheAction = NULL; | ||
| 2424 | } | ||
| 2425 | |||
| 2426 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction); | ||
| 2427 | Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || | ||
| 2428 | BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type || | ||
| 2429 | BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type); | ||
| 2430 | |||
| 2431 | hr = AppendCacheAction(pPlan, &pCacheAction); | ||
| 2432 | ExitOnFailure(hr, "Failed to append cache action to cache payload."); | ||
| 2433 | |||
| 2434 | hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress); | ||
| 2435 | ExitOnFailure(hr, "Failed to create payload progress."); | ||
| 2436 | |||
| 2437 | if (!wzLayoutDirectory) | ||
| 2438 | { | ||
| 2439 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD; | ||
| 2440 | pCacheAction->cachePayload.pPackage = pPackage; | ||
| 2441 | pCacheAction->cachePayload.pPayload = pPayload; | ||
| 2442 | pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex; | ||
| 2443 | pCacheAction->cachePayload.fMove = TRUE; | ||
| 2444 | pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction; | ||
| 2445 | pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath; | ||
| 2446 | sczPayloadWorkingPath = NULL; | ||
| 2447 | } | ||
| 2448 | else | ||
| 2449 | { | ||
| 2450 | hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0); | ||
| 2451 | ExitOnFailure(hr, "Failed to copy layout directory into plan."); | ||
| 2452 | |||
| 2453 | pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD; | ||
| 2454 | pCacheAction->layoutPayload.pPackage = pPackage; | ||
| 2455 | pCacheAction->layoutPayload.pPayload = pPayload; | ||
| 2456 | pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex; | ||
| 2457 | pCacheAction->layoutPayload.fMove = TRUE; | ||
| 2458 | pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction; | ||
| 2459 | pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath; | ||
| 2460 | sczPayloadWorkingPath = NULL; | ||
| 2461 | } | ||
| 2462 | |||
| 2463 | pCacheAction = NULL; | ||
| 2464 | |||
| 2465 | LExit: | ||
| 2466 | ReleaseStr(sczPayloadWorkingPath); | ||
| 2467 | |||
| 2468 | return hr; | ||
| 2469 | } | ||
| 2470 | |||
| 2471 | static BOOL FindContainerCacheAction( | ||
| 2472 | __in BURN_CACHE_ACTION_TYPE type, | ||
| 2473 | __in BURN_PLAN* pPlan, | ||
| 2474 | __in BURN_CONTAINER* pContainer, | ||
| 2475 | __in DWORD iSearchStart, | ||
| 2476 | __in DWORD iSearchEnd, | ||
| 2477 | __out_opt BURN_CACHE_ACTION** ppCacheAction, | ||
| 2478 | __out_opt DWORD* piCacheAction | ||
| 2479 | ) | ||
| 2480 | { | ||
| 2481 | BOOL fFound = FALSE; // assume we won't find what we are looking for. | ||
| 2482 | |||
| 2483 | Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type); | ||
| 2484 | |||
| 2485 | iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart; | ||
| 2486 | iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd; | ||
| 2487 | |||
| 2488 | for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch) | ||
| 2489 | { | ||
| 2490 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch; | ||
| 2491 | if (pCacheAction->type == type && | ||
| 2492 | ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) || | ||
| 2493 | (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer))) | ||
| 2494 | { | ||
| 2495 | if (ppCacheAction) | ||
| 2496 | { | ||
| 2497 | *ppCacheAction = pCacheAction; | ||
| 2498 | } | ||
| 2499 | |||
| 2500 | if (piCacheAction) | ||
| 2501 | { | ||
| 2502 | *piCacheAction = iSearch; | ||
| 2503 | } | ||
| 2504 | |||
| 2505 | fFound = TRUE; | ||
| 2506 | break; | ||
| 2507 | } | ||
| 2508 | } | ||
| 2509 | |||
| 2510 | return fFound; | ||
| 2511 | } | ||
| 2512 | |||
| 2513 | static HRESULT CreateContainerAcquireAndExtractAction( | ||
| 2514 | __in BURN_PLAN* pPlan, | ||
| 2515 | __in BURN_CONTAINER* pContainer, | ||
| 2516 | __in DWORD iPackageStartAction, | ||
| 2517 | __in BOOL fPayloadCached, | ||
| 2518 | __out BURN_CACHE_ACTION** ppContainerExtractAction, | ||
| 2519 | __out DWORD* piContainerTryAgainAction | ||
| 2520 | ) | ||
| 2521 | { | ||
| 2522 | HRESULT hr = S_OK; | ||
| 2523 | DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2524 | BURN_CACHE_ACTION* pContainerExtractAction = NULL; | ||
| 2525 | DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2526 | DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX; | ||
| 2527 | LPWSTR sczContainerWorkingPath = NULL; | ||
| 2528 | |||
| 2529 | // If the container is actually attached to the executable then we will not need an acquire | ||
| 2530 | // container action. | ||
| 2531 | if (!pContainer->fActuallyAttached) | ||
| 2532 | { | ||
| 2533 | BURN_CACHE_ACTION* pAcquireContainerAction = NULL; | ||
| 2534 | |||
| 2535 | // If there is no plan to acquire the container then add acquire action since we | ||
| 2536 | // can't extract stuff out of a container until we acquire the container. | ||
| 2537 | if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction)) | ||
| 2538 | { | ||
| 2539 | hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction); | ||
| 2540 | ExitOnFailure(hr, "Failed to append acquire container action to plan."); | ||
| 2541 | |||
| 2542 | pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong. | ||
| 2543 | } | ||
| 2544 | |||
| 2545 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); | ||
| 2546 | Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type); | ||
| 2547 | Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer); | ||
| 2548 | } | ||
| 2549 | |||
| 2550 | Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) || | ||
| 2551 | (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction)); | ||
| 2552 | |||
| 2553 | // If we do not find an action for extracting payloads from this container, create it now. | ||
| 2554 | if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction)) | ||
| 2555 | { | ||
| 2556 | // Attached containers that are actually attached use the executable path for their working path. | ||
| 2557 | if (pContainer->fActuallyAttached) | ||
| 2558 | { | ||
| 2559 | Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction); | ||
| 2560 | |||
| 2561 | hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL); | ||
| 2562 | ExitOnFailure(hr, "Failed to get path for executing module as attached container working path."); | ||
| 2563 | } | ||
| 2564 | else // use the acquired working path as the location of the container. | ||
| 2565 | { | ||
| 2566 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction); | ||
| 2567 | |||
| 2568 | hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0); | ||
| 2569 | ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container."); | ||
| 2570 | } | ||
| 2571 | |||
| 2572 | hr = AppendCacheAction(pPlan, &pContainerExtractAction); | ||
| 2573 | ExitOnFailure(hr, "Failed to append cache action to extract payloads from container."); | ||
| 2574 | |||
| 2575 | iExtractAction = pPlan->cCacheActions - 1; | ||
| 2576 | |||
| 2577 | pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER; | ||
| 2578 | pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong. | ||
| 2579 | pContainerExtractAction->extractContainer.pContainer = pContainer; | ||
| 2580 | pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction; | ||
| 2581 | pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath; | ||
| 2582 | sczContainerWorkingPath = NULL; | ||
| 2583 | } | ||
| 2584 | |||
| 2585 | Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type); | ||
| 2586 | Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction); | ||
| 2587 | |||
| 2588 | // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action. | ||
| 2589 | iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction; | ||
| 2590 | |||
| 2591 | // If the try again action thinks it can be skipped but the payload is not cached, | ||
| 2592 | // ensure the action will not be skipped. | ||
| 2593 | BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction; | ||
| 2594 | Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) || | ||
| 2595 | (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer)); | ||
| 2596 | if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached) | ||
| 2597 | { | ||
| 2598 | pTryAgainAction->fSkipUntilRetried = FALSE; | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | *ppContainerExtractAction = pContainerExtractAction; | ||
| 2602 | *piContainerTryAgainAction = iTryAgainAction; | ||
| 2603 | |||
| 2604 | LExit: | ||
| 2605 | ReleaseStr(sczContainerWorkingPath); | ||
| 2606 | |||
| 2607 | return hr; | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | static HRESULT AddAcquireContainer( | ||
| 2611 | __in BURN_PLAN* pPlan, | ||
| 2612 | __in BURN_CONTAINER* pContainer, | ||
| 2613 | __out_opt BURN_CACHE_ACTION** ppCacheAction, | ||
| 2614 | __out_opt DWORD* piCacheAction | ||
| 2615 | ) | ||
| 2616 | { | ||
| 2617 | HRESULT hr = S_OK; | ||
| 2618 | LPWSTR sczContainerWorkingPath = NULL; | ||
| 2619 | BURN_CACHE_ACTION* pAcquireContainerAction = NULL; | ||
| 2620 | BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; | ||
| 2621 | |||
| 2622 | hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath); | ||
| 2623 | ExitOnFailure(hr, "Failed to calculate unverified path for container."); | ||
| 2624 | |||
| 2625 | hr = AppendCacheAction(pPlan, &pAcquireContainerAction); | ||
| 2626 | ExitOnFailure(hr, "Failed to append acquire container action to plan."); | ||
| 2627 | |||
| 2628 | hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress); | ||
| 2629 | ExitOnFailure(hr, "Failed to create container progress."); | ||
| 2630 | |||
| 2631 | pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER; | ||
| 2632 | pAcquireContainerAction->resolveContainer.pContainer = pContainer; | ||
| 2633 | pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex; | ||
| 2634 | pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath; | ||
| 2635 | sczContainerWorkingPath = NULL; | ||
| 2636 | |||
| 2637 | if (ppCacheAction) | ||
| 2638 | { | ||
| 2639 | *ppCacheAction = pAcquireContainerAction; | ||
| 2640 | } | ||
| 2641 | |||
| 2642 | if (piCacheAction) | ||
| 2643 | { | ||
| 2644 | *piCacheAction = pPlan->cCacheActions - 1; | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | LExit: | ||
| 2648 | ReleaseStr(sczContainerWorkingPath); | ||
| 2649 | |||
| 2650 | return hr; | ||
| 2651 | } | ||
| 2652 | |||
| 2653 | static HRESULT AddExtractPayload( | ||
| 2654 | __in BURN_CACHE_ACTION* pCacheAction, | ||
| 2655 | __in_opt BURN_PACKAGE* pPackage, | ||
| 2656 | __in BURN_PAYLOAD* pPayload, | ||
| 2657 | __in_z LPCWSTR wzPayloadWorkingPath | ||
| 2658 | ) | ||
| 2659 | { | ||
| 2660 | HRESULT hr = S_OK; | ||
| 2661 | |||
| 2662 | Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type); | ||
| 2663 | |||
| 2664 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5); | ||
| 2665 | ExitOnFailure(hr, "Failed to grow list of payloads to extract from container."); | ||
| 2666 | |||
| 2667 | BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads; | ||
| 2668 | pExtractPayload->pPackage = pPackage; | ||
| 2669 | pExtractPayload->pPayload = pPayload; | ||
| 2670 | hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0); | ||
| 2671 | ExitOnFailure(hr, "Failed to copy unverified path for payload to extract."); | ||
| 2672 | ++pCacheAction->extractContainer.cPayloads; | ||
| 2673 | |||
| 2674 | LExit: | ||
| 2675 | return hr; | ||
| 2676 | } | ||
| 2677 | |||
| 2678 | static BURN_CACHE_ACTION* ProcessSharedPayload( | ||
| 2679 | __in BURN_PLAN* pPlan, | ||
| 2680 | __in BURN_PAYLOAD* pPayload | ||
| 2681 | ) | ||
| 2682 | { | ||
| 2683 | BURN_CACHE_ACTION* pAcquireAction = NULL; | ||
| 2684 | #ifdef DEBUG | ||
| 2685 | DWORD cMove = 0; | ||
| 2686 | #endif | ||
| 2687 | |||
| 2688 | for (DWORD i = 0; i < pPlan->cCacheActions; ++i) | ||
| 2689 | { | ||
| 2690 | BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i; | ||
| 2691 | |||
| 2692 | if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type && | ||
| 2693 | pCacheAction->resolvePayload.pPayload == pPayload) | ||
| 2694 | { | ||
| 2695 | AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload."); | ||
| 2696 | pAcquireAction = pCacheAction; | ||
| 2697 | } | ||
| 2698 | else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type && | ||
| 2699 | pCacheAction->cachePayload.pPayload == pPayload && | ||
| 2700 | pCacheAction->cachePayload.fMove) | ||
| 2701 | { | ||
| 2702 | // Since we found a shared payload, change its operation from MOVE to COPY. | ||
| 2703 | pCacheAction->cachePayload.fMove = FALSE; | ||
| 2704 | |||
| 2705 | AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); | ||
| 2706 | #ifndef DEBUG | ||
| 2707 | break; | ||
| 2708 | #endif | ||
| 2709 | } | ||
| 2710 | else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type && | ||
| 2711 | pCacheAction->layoutPayload.pPayload == pPayload && | ||
| 2712 | pCacheAction->layoutPayload.fMove) | ||
| 2713 | { | ||
| 2714 | // Since we found a shared payload, change its operation from MOVE to COPY if necessary | ||
| 2715 | pCacheAction->layoutPayload.fMove = FALSE; | ||
| 2716 | |||
| 2717 | AssertSz(1 == ++cMove, "Shared payload should be moved once and only once."); | ||
| 2718 | #ifndef DEBUG | ||
| 2719 | break; | ||
| 2720 | #endif | ||
| 2721 | } | ||
| 2722 | } | ||
| 2723 | |||
| 2724 | return pAcquireAction; | ||
| 2725 | } | ||
| 2726 | |||
| 2727 | static HRESULT RemoveUnnecessaryActions( | ||
| 2728 | __in BOOL fExecute, | ||
| 2729 | __in BURN_EXECUTE_ACTION* rgActions, | ||
| 2730 | __in DWORD cActions | ||
| 2731 | ) | ||
| 2732 | { | ||
| 2733 | HRESULT hr = S_OK; | ||
| 2734 | LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback"; | ||
| 2735 | |||
| 2736 | for (DWORD i = 0; i < cActions; ++i) | ||
| 2737 | { | ||
| 2738 | BURN_EXECUTE_ACTION* pAction = rgActions + i; | ||
| 2739 | |||
| 2740 | // If this MSP targets a package in the chain, check the target's execute state | ||
| 2741 | // to see if this patch should be skipped. | ||
| 2742 | if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage) | ||
| 2743 | { | ||
| 2744 | BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback; | ||
| 2745 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction) | ||
| 2746 | { | ||
| 2747 | LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); | ||
| 2748 | pAction->fDeleted = TRUE; | ||
| 2749 | } | ||
| 2750 | else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action) | ||
| 2751 | { | ||
| 2752 | // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip | ||
| 2753 | // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being | ||
| 2754 | // repaired, skip this operation since it will be redundant. | ||
| 2755 | // | ||
| 2756 | // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI | ||
| 2757 | // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired. | ||
| 2758 | if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action) | ||
| 2759 | { | ||
| 2760 | LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback); | ||
| 2761 | pAction->fDeleted = TRUE; | ||
| 2762 | } | ||
| 2763 | } | ||
| 2764 | } | ||
| 2765 | } | ||
| 2766 | |||
| 2767 | return hr; | ||
| 2768 | } | ||
| 2769 | |||
| 2770 | static HRESULT FinalizeSlipstreamPatchActions( | ||
| 2771 | __in BOOL fExecute, | ||
| 2772 | __in BURN_EXECUTE_ACTION* rgActions, | ||
| 2773 | __in DWORD cActions | ||
| 2774 | ) | ||
| 2775 | { | ||
| 2776 | HRESULT hr = S_OK; | ||
| 2777 | |||
| 2778 | for (DWORD i = 0; i < cActions; ++i) | ||
| 2779 | { | ||
| 2780 | BURN_EXECUTE_ACTION* pAction = rgActions + i; | ||
| 2781 | |||
| 2782 | // If this MSI package contains slipstream patches store the slipstream actions. | ||
| 2783 | if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages) | ||
| 2784 | { | ||
| 2785 | BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; | ||
| 2786 | |||
| 2787 | // By default all slipstream actions will be initialized to "no action" (aka: 0). | ||
| 2788 | pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE); | ||
| 2789 | ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions."); | ||
| 2790 | |||
| 2791 | // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot | ||
| 2792 | // be applied right now. | ||
| 2793 | if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action) | ||
| 2794 | { | ||
| 2795 | for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) | ||
| 2796 | { | ||
| 2797 | BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j]; | ||
| 2798 | AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches."); | ||
| 2799 | |||
| 2800 | pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback; | ||
| 2801 | for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k) | ||
| 2802 | { | ||
| 2803 | BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k; | ||
| 2804 | if (pPackage == pTargetProduct->pChainedTargetPackage) | ||
| 2805 | { | ||
| 2806 | pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback; | ||
| 2807 | break; | ||
| 2808 | } | ||
| 2809 | } | ||
| 2810 | } | ||
| 2811 | } | ||
| 2812 | } | ||
| 2813 | } | ||
| 2814 | |||
| 2815 | LExit: | ||
| 2816 | return hr; | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | static HRESULT PlanDependencyActions( | ||
| 2820 | __in BOOL fBundlePerMachine, | ||
| 2821 | __in BURN_PLAN* pPlan, | ||
| 2822 | __in BURN_PACKAGE* pPackage | ||
| 2823 | ) | ||
| 2824 | { | ||
| 2825 | HRESULT hr = S_OK; | ||
| 2826 | |||
| 2827 | hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan); | ||
| 2828 | ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId); | ||
| 2829 | |||
| 2830 | hr = DependencyPlanPackage(NULL, pPackage, pPlan); | ||
| 2831 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
| 2832 | |||
| 2833 | hr = DependencyPlanPackageComplete(pPackage, pPlan); | ||
| 2834 | ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); | ||
| 2835 | |||
| 2836 | LExit: | ||
| 2837 | return hr; | ||
| 2838 | } | ||
| 2839 | |||
| 2840 | static HRESULT CalculateExecuteActions( | ||
| 2841 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 2842 | __in BURN_PACKAGE* pPackage, | ||
| 2843 | __in BURN_VARIABLES* pVariables, | ||
| 2844 | __out_opt BOOL* pfBARequestedCache | ||
| 2845 | ) | ||
| 2846 | { | ||
| 2847 | HRESULT hr = S_OK; | ||
| 2848 | |||
| 2849 | // Calculate execute actions. | ||
| 2850 | switch (pPackage->type) | ||
| 2851 | { | ||
| 2852 | case BURN_PACKAGE_TYPE_EXE: | ||
| 2853 | hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache); | ||
| 2854 | break; | ||
| 2855 | |||
| 2856 | case BURN_PACKAGE_TYPE_MSI: | ||
| 2857 | hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache); | ||
| 2858 | break; | ||
| 2859 | |||
| 2860 | case BURN_PACKAGE_TYPE_MSP: | ||
| 2861 | hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache); | ||
| 2862 | break; | ||
| 2863 | |||
| 2864 | case BURN_PACKAGE_TYPE_MSU: | ||
| 2865 | hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache); | ||
| 2866 | break; | ||
| 2867 | |||
| 2868 | default: | ||
| 2869 | hr = E_UNEXPECTED; | ||
| 2870 | ExitOnFailure(hr, "Invalid package type."); | ||
| 2871 | } | ||
| 2872 | |||
| 2873 | LExit: | ||
| 2874 | return hr; | ||
| 2875 | } | ||
| 2876 | |||
| 2877 | static BOOL NeedsCache( | ||
| 2878 | __in BURN_PLAN* pPlan, | ||
| 2879 | __in BURN_PACKAGE* pPackage | ||
| 2880 | ) | ||
| 2881 | { | ||
| 2882 | // All packages that have cacheType set to always should be cached if the bundle is going to be present. | ||
| 2883 | if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action) | ||
| 2884 | { | ||
| 2885 | return TRUE; | ||
| 2886 | } | ||
| 2887 | else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall). | ||
| 2888 | { | ||
| 2889 | return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute; | ||
| 2890 | } | ||
| 2891 | else // The other package types can uninstall without the original package. | ||
| 2892 | { | ||
| 2893 | return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute; | ||
| 2894 | } | ||
| 2895 | } | ||
| 2896 | |||
| 2897 | static HRESULT CreateContainerProgress( | ||
| 2898 | __in BURN_PLAN* pPlan, | ||
| 2899 | __in BURN_CONTAINER* pContainer, | ||
| 2900 | __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress | ||
| 2901 | ) | ||
| 2902 | { | ||
| 2903 | HRESULT hr = S_OK; | ||
| 2904 | BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL; | ||
| 2905 | |||
| 2906 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5); | ||
| 2907 | ExitOnFailure(hr, "Failed to grow container progress list."); | ||
| 2908 | |||
| 2909 | if (!pPlan->shContainerProgress) | ||
| 2910 | { | ||
| 2911 | hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast<void **>(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE); | ||
| 2912 | ExitOnFailure(hr, "Failed to create container progress dictionary."); | ||
| 2913 | } | ||
| 2914 | |||
| 2915 | hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast<void **>(&pContainerProgress)); | ||
| 2916 | if (E_NOTFOUND == hr) | ||
| 2917 | { | ||
| 2918 | pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress]; | ||
| 2919 | pContainerProgress->iIndex = pPlan->cContainerProgress; | ||
| 2920 | pContainerProgress->pContainer = pContainer; | ||
| 2921 | pContainerProgress->wzId = pContainer->sczId; | ||
| 2922 | |||
| 2923 | hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress); | ||
| 2924 | ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId); | ||
| 2925 | |||
| 2926 | ++pPlan->cContainerProgress; | ||
| 2927 | pPlan->qwCacheSizeTotal += pContainer->qwFileSize; | ||
| 2928 | } | ||
| 2929 | ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId); | ||
| 2930 | |||
| 2931 | *ppContainerProgress = pContainerProgress; | ||
| 2932 | |||
| 2933 | LExit: | ||
| 2934 | return hr; | ||
| 2935 | } | ||
| 2936 | |||
| 2937 | static HRESULT CreatePayloadProgress( | ||
| 2938 | __in BURN_PLAN* pPlan, | ||
| 2939 | __in BURN_PAYLOAD* pPayload, | ||
| 2940 | __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress | ||
| 2941 | ) | ||
| 2942 | { | ||
| 2943 | HRESULT hr = S_OK; | ||
| 2944 | BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL; | ||
| 2945 | |||
| 2946 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5); | ||
| 2947 | ExitOnFailure(hr, "Failed to grow payload progress list."); | ||
| 2948 | |||
| 2949 | if (!pPlan->shPayloadProgress) | ||
| 2950 | { | ||
| 2951 | hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast<void **>(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE); | ||
| 2952 | ExitOnFailure(hr, "Failed to create payload progress dictionary."); | ||
| 2953 | } | ||
| 2954 | |||
| 2955 | hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast<void **>(&pPayloadProgress)); | ||
| 2956 | if (E_NOTFOUND == hr) | ||
| 2957 | { | ||
| 2958 | pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress]; | ||
| 2959 | pPayloadProgress->iIndex = pPlan->cPayloadProgress; | ||
| 2960 | pPayloadProgress->pPayload = pPayload; | ||
| 2961 | pPayloadProgress->wzId = pPayload->sczKey; | ||
| 2962 | |||
| 2963 | hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress); | ||
| 2964 | ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId); | ||
| 2965 | |||
| 2966 | ++pPlan->cPayloadProgress; | ||
| 2967 | pPlan->qwCacheSizeTotal += pPayload->qwFileSize; | ||
| 2968 | } | ||
| 2969 | ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey); | ||
| 2970 | |||
| 2971 | *ppPayloadProgress = pPayloadProgress; | ||
| 2972 | |||
| 2973 | LExit: | ||
| 2974 | return hr; | ||
| 2975 | } | ||
| 2976 | |||
| 2977 | |||
| 2978 | #ifdef DEBUG | ||
| 2979 | |||
| 2980 | static void CacheActionLog( | ||
| 2981 | __in DWORD iAction, | ||
| 2982 | __in BURN_CACHE_ACTION* pAction, | ||
| 2983 | __in BOOL fRollback | ||
| 2984 | ) | ||
| 2985 | { | ||
| 2986 | LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache"; | ||
| 2987 | switch (pAction->type) | ||
| 2988 | { | ||
| 2989 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER: | ||
| 2990 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 2991 | break; | ||
| 2992 | |||
| 2993 | case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD: | ||
| 2994 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 2995 | break; | ||
| 2996 | |||
| 2997 | case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD: | ||
| 2998 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction); | ||
| 2999 | break; | ||
| 3000 | |||
| 3001 | case BURN_CACHE_ACTION_TYPE_CHECKPOINT: | ||
| 3002 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); | ||
| 3003 | break; | ||
| 3004 | |||
| 3005 | case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER: | ||
| 3006 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction); | ||
| 3007 | for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++) | ||
| 3008 | { | ||
| 3009 | LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath); | ||
| 3010 | } | ||
| 3011 | break; | ||
| 3012 | |||
| 3013 | case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE: | ||
| 3014 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 3015 | break; | ||
| 3016 | |||
| 3017 | case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER: | ||
| 3018 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction); | ||
| 3019 | break; | ||
| 3020 | |||
| 3021 | case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD: | ||
| 3022 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction); | ||
| 3023 | break; | ||
| 3024 | |||
| 3025 | case BURN_CACHE_ACTION_TYPE_PACKAGE_START: | ||
| 3026 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 3027 | break; | ||
| 3028 | |||
| 3029 | case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP: | ||
| 3030 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 3031 | break; | ||
| 3032 | |||
| 3033 | case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE: | ||
| 3034 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 3035 | break; | ||
| 3036 | |||
| 3037 | case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT: | ||
| 3038 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried)); | ||
| 3039 | break; | ||
| 3040 | |||
| 3041 | case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY: | ||
| 3042 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no"); | ||
| 3043 | break; | ||
| 3044 | |||
| 3045 | default: | ||
| 3046 | AssertSz(FALSE, "Unknown cache action type."); | ||
| 3047 | break; | ||
| 3048 | } | ||
| 3049 | } | ||
| 3050 | |||
| 3051 | static void ExecuteActionLog( | ||
| 3052 | __in DWORD iAction, | ||
| 3053 | __in BURN_EXECUTE_ACTION* pAction, | ||
| 3054 | __in BOOL fRollback | ||
| 3055 | ) | ||
| 3056 | { | ||
| 3057 | LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute"; | ||
| 3058 | switch (pAction->type) | ||
| 3059 | { | ||
| 3060 | case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT: | ||
| 3061 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId); | ||
| 3062 | break; | ||
| 3063 | |||
| 3064 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER: | ||
| 3065 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action); | ||
| 3066 | break; | ||
| 3067 | |||
| 3068 | case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: | ||
| 3069 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action); | ||
| 3070 | break; | ||
| 3071 | |||
| 3072 | case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: | ||
| 3073 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies); | ||
| 3074 | break; | ||
| 3075 | |||
| 3076 | case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: | ||
| 3077 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes); | ||
| 3078 | for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j) | ||
| 3079 | { | ||
| 3080 | LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId); | ||
| 3081 | } | ||
| 3082 | break; | ||
| 3083 | |||
| 3084 | case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: | ||
| 3085 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath); | ||
| 3086 | for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) | ||
| 3087 | { | ||
| 3088 | LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId); | ||
| 3089 | } | ||
| 3090 | break; | ||
| 3091 | |||
| 3092 | case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: | ||
| 3093 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath); | ||
| 3094 | break; | ||
| 3095 | |||
| 3096 | case BURN_EXECUTE_ACTION_TYPE_REGISTRATION: | ||
| 3097 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no"); | ||
| 3098 | break; | ||
| 3099 | |||
| 3100 | case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY: | ||
| 3101 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no"); | ||
| 3102 | break; | ||
| 3103 | |||
| 3104 | case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT: | ||
| 3105 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent); | ||
| 3106 | break; | ||
| 3107 | |||
| 3108 | case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE: | ||
| 3109 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId); | ||
| 3110 | break; | ||
| 3111 | |||
| 3112 | case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE: | ||
| 3113 | LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode); | ||
| 3114 | break; | ||
| 3115 | |||
| 3116 | default: | ||
| 3117 | AssertSz(FALSE, "Unknown execute action type."); | ||
| 3118 | break; | ||
| 3119 | } | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | extern "C" void PlanDump( | ||
| 3123 | __in BURN_PLAN* pPlan | ||
| 3124 | ) | ||
| 3125 | { | ||
| 3126 | LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---"); | ||
| 3127 | |||
| 3128 | LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action)); | ||
| 3129 | LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine)); | ||
| 3130 | LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault)); | ||
| 3131 | LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize); | ||
| 3132 | |||
| 3133 | LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal); | ||
| 3134 | for (DWORD i = 0; i < pPlan->cCacheActions; ++i) | ||
| 3135 | { | ||
| 3136 | CacheActionLog(i, pPlan->rgCacheActions + i, FALSE); | ||
| 3137 | } | ||
| 3138 | |||
| 3139 | for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i) | ||
| 3140 | { | ||
| 3141 | CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE); | ||
| 3142 | } | ||
| 3143 | |||
| 3144 | LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal); | ||
| 3145 | LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal); | ||
| 3146 | for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) | ||
| 3147 | { | ||
| 3148 | ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE); | ||
| 3149 | } | ||
| 3150 | |||
| 3151 | for (DWORD i = 0; i < pPlan->cRollbackActions; ++i) | ||
| 3152 | { | ||
| 3153 | ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE); | ||
| 3154 | } | ||
| 3155 | |||
| 3156 | for (DWORD i = 0; i < pPlan->cCleanActions; ++i) | ||
| 3157 | { | ||
| 3158 | LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); | ||
| 3159 | } | ||
| 3160 | |||
| 3161 | for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) | ||
| 3162 | { | ||
| 3163 | LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName); | ||
| 3164 | } | ||
| 3165 | |||
| 3166 | LogStringLine(REPORT_STANDARD, "--- End plan dump ---"); | ||
| 3167 | } | ||
| 3168 | |||
| 3169 | #endif | ||
diff --git a/src/engine/plan.h b/src/engine/plan.h new file mode 100644 index 00000000..54cfe59d --- /dev/null +++ b/src/engine/plan.h | |||
| @@ -0,0 +1,543 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; | ||
| 13 | |||
| 14 | enum BURN_REGISTRATION_ACTION_OPERATIONS | ||
| 15 | { | ||
| 16 | BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0, | ||
| 17 | BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1, | ||
| 18 | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2, | ||
| 19 | BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum BURN_DEPENDENCY_REGISTRATION_ACTION | ||
| 23 | { | ||
| 24 | BURN_DEPENDENCY_REGISTRATION_ACTION_NONE, | ||
| 25 | BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, | ||
| 26 | BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE | ||
| 30 | { | ||
| 31 | BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE, | ||
| 32 | BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, | ||
| 33 | BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum BURN_CACHE_ACTION_TYPE | ||
| 37 | { | ||
| 38 | BURN_CACHE_ACTION_TYPE_NONE, | ||
| 39 | BURN_CACHE_ACTION_TYPE_CHECKPOINT, | ||
| 40 | BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE, | ||
| 41 | BURN_CACHE_ACTION_TYPE_PACKAGE_START, | ||
| 42 | BURN_CACHE_ACTION_TYPE_PACKAGE_STOP, | ||
| 43 | BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, | ||
| 44 | BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, | ||
| 45 | BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, | ||
| 46 | BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, | ||
| 47 | BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER, | ||
| 48 | BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD, | ||
| 49 | BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD, | ||
| 50 | BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD, | ||
| 51 | BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum BURN_EXECUTE_ACTION_TYPE | ||
| 55 | { | ||
| 56 | BURN_EXECUTE_ACTION_TYPE_NONE, | ||
| 57 | BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, | ||
| 58 | BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, | ||
| 59 | BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, | ||
| 60 | BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, | ||
| 61 | BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, | ||
| 62 | BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, | ||
| 63 | BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE, | ||
| 64 | BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP, | ||
| 65 | BURN_EXECUTE_ACTION_TYPE_SERVICE_START, | ||
| 66 | BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, | ||
| 67 | BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, | ||
| 68 | BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, | ||
| 69 | BURN_EXECUTE_ACTION_TYPE_REGISTRATION, | ||
| 70 | BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE, | ||
| 71 | }; | ||
| 72 | |||
| 73 | enum BURN_CLEAN_ACTION_TYPE | ||
| 74 | { | ||
| 75 | BURN_CLEAN_ACTION_TYPE_NONE, | ||
| 76 | BURN_CLEAN_ACTION_TYPE_BUNDLE, | ||
| 77 | BURN_CLEAN_ACTION_TYPE_PACKAGE, | ||
| 78 | }; | ||
| 79 | |||
| 80 | |||
| 81 | // structs | ||
| 82 | |||
| 83 | typedef struct _BURN_EXTRACT_PAYLOAD | ||
| 84 | { | ||
| 85 | BURN_PACKAGE* pPackage; | ||
| 86 | BURN_PAYLOAD* pPayload; | ||
| 87 | LPWSTR sczUnverifiedPath; | ||
| 88 | } BURN_EXTRACT_PAYLOAD; | ||
| 89 | |||
| 90 | typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION | ||
| 91 | { | ||
| 92 | BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type; | ||
| 93 | LPWSTR sczBundleId; | ||
| 94 | LPWSTR sczDependentProviderKey; | ||
| 95 | } BURN_DEPENDENT_REGISTRATION_ACTION; | ||
| 96 | |||
| 97 | typedef struct _BURN_CACHE_CONTAINER_PROGRESS | ||
| 98 | { | ||
| 99 | LPWSTR wzId; | ||
| 100 | DWORD iIndex; | ||
| 101 | BOOL fCachedDuringApply; | ||
| 102 | BURN_CONTAINER* pContainer; | ||
| 103 | } BURN_CACHE_CONTAINER_PROGRESS; | ||
| 104 | |||
| 105 | typedef struct _BURN_CACHE_PAYLOAD_PROGRESS | ||
| 106 | { | ||
| 107 | LPWSTR wzId; | ||
| 108 | DWORD iIndex; | ||
| 109 | BOOL fCachedDuringApply; | ||
| 110 | BURN_PAYLOAD* pPayload; | ||
| 111 | } BURN_CACHE_PAYLOAD_PROGRESS; | ||
| 112 | |||
| 113 | typedef struct _BURN_CACHE_ACTION | ||
| 114 | { | ||
| 115 | BURN_CACHE_ACTION_TYPE type; | ||
| 116 | BOOL fSkipUntilRetried; | ||
| 117 | union | ||
| 118 | { | ||
| 119 | struct | ||
| 120 | { | ||
| 121 | DWORD dwId; | ||
| 122 | } checkpoint; | ||
| 123 | struct | ||
| 124 | { | ||
| 125 | LPWSTR sczExecutableName; | ||
| 126 | LPWSTR sczLayoutDirectory; | ||
| 127 | LPWSTR sczUnverifiedPath; | ||
| 128 | DWORD64 qwBundleSize; | ||
| 129 | } bundleLayout; | ||
| 130 | struct | ||
| 131 | { | ||
| 132 | BURN_PACKAGE* pPackage; | ||
| 133 | DWORD cCachePayloads; | ||
| 134 | DWORD64 qwCachePayloadSizeTotal; | ||
| 135 | DWORD iPackageCompleteAction; | ||
| 136 | } packageStart; | ||
| 137 | struct | ||
| 138 | { | ||
| 139 | BURN_PACKAGE* pPackage; | ||
| 140 | } packageStop; | ||
| 141 | struct | ||
| 142 | { | ||
| 143 | BURN_PACKAGE* pPackage; | ||
| 144 | } rollbackPackage; | ||
| 145 | struct | ||
| 146 | { | ||
| 147 | HANDLE hEvent; | ||
| 148 | } syncpoint; | ||
| 149 | struct | ||
| 150 | { | ||
| 151 | BURN_CONTAINER* pContainer; | ||
| 152 | DWORD iProgress; | ||
| 153 | LPWSTR sczUnverifiedPath; | ||
| 154 | } resolveContainer; | ||
| 155 | struct | ||
| 156 | { | ||
| 157 | BURN_CONTAINER* pContainer; | ||
| 158 | DWORD iSkipUntilAcquiredByAction; | ||
| 159 | LPWSTR sczContainerUnverifiedPath; | ||
| 160 | |||
| 161 | BURN_EXTRACT_PAYLOAD* rgPayloads; | ||
| 162 | DWORD cPayloads; | ||
| 163 | } extractContainer; | ||
| 164 | struct | ||
| 165 | { | ||
| 166 | BURN_PACKAGE* pPackage; | ||
| 167 | BURN_CONTAINER* pContainer; | ||
| 168 | DWORD iProgress; | ||
| 169 | DWORD iTryAgainAction; | ||
| 170 | DWORD cTryAgainAttempts; | ||
| 171 | LPWSTR sczLayoutDirectory; | ||
| 172 | LPWSTR sczUnverifiedPath; | ||
| 173 | BOOL fMove; | ||
| 174 | } layoutContainer; | ||
| 175 | struct | ||
| 176 | { | ||
| 177 | BURN_PACKAGE* pPackage; | ||
| 178 | BURN_PAYLOAD* pPayload; | ||
| 179 | DWORD iProgress; | ||
| 180 | LPWSTR sczUnverifiedPath; | ||
| 181 | } resolvePayload; | ||
| 182 | struct | ||
| 183 | { | ||
| 184 | BURN_PACKAGE* pPackage; | ||
| 185 | BURN_PAYLOAD* pPayload; | ||
| 186 | DWORD iProgress; | ||
| 187 | DWORD iTryAgainAction; | ||
| 188 | DWORD cTryAgainAttempts; | ||
| 189 | LPWSTR sczUnverifiedPath; | ||
| 190 | BOOL fMove; | ||
| 191 | } cachePayload; | ||
| 192 | struct | ||
| 193 | { | ||
| 194 | BURN_PACKAGE* pPackage; | ||
| 195 | BURN_PAYLOAD* pPayload; | ||
| 196 | DWORD iProgress; | ||
| 197 | DWORD iTryAgainAction; | ||
| 198 | DWORD cTryAgainAttempts; | ||
| 199 | LPWSTR sczLayoutDirectory; | ||
| 200 | LPWSTR sczUnverifiedPath; | ||
| 201 | BOOL fMove; | ||
| 202 | } layoutPayload; | ||
| 203 | struct | ||
| 204 | { | ||
| 205 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; | ||
| 206 | HANDLE hEvent; | ||
| 207 | } rollbackBoundary; | ||
| 208 | }; | ||
| 209 | } BURN_CACHE_ACTION; | ||
| 210 | |||
| 211 | typedef struct _BURN_ORDERED_PATCHES | ||
| 212 | { | ||
| 213 | DWORD dwOrder; | ||
| 214 | BURN_PACKAGE* pPackage; | ||
| 215 | } BURN_ORDERED_PATCHES; | ||
| 216 | |||
| 217 | typedef struct _BURN_EXECUTE_ACTION | ||
| 218 | { | ||
| 219 | BURN_EXECUTE_ACTION_TYPE type; | ||
| 220 | BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard. | ||
| 221 | union | ||
| 222 | { | ||
| 223 | struct | ||
| 224 | { | ||
| 225 | DWORD dwId; | ||
| 226 | } checkpoint; | ||
| 227 | struct | ||
| 228 | { | ||
| 229 | HANDLE hEvent; | ||
| 230 | } syncpoint; | ||
| 231 | struct | ||
| 232 | { | ||
| 233 | BURN_PACKAGE* pPackage; | ||
| 234 | } uncachePackage; | ||
| 235 | struct | ||
| 236 | { | ||
| 237 | BURN_PACKAGE* pPackage; | ||
| 238 | BOOL fFireAndForget; | ||
| 239 | BOOTSTRAPPER_ACTION_STATE action; | ||
| 240 | LPWSTR sczIgnoreDependencies; | ||
| 241 | LPWSTR sczAncestors; | ||
| 242 | } exePackage; | ||
| 243 | struct | ||
| 244 | { | ||
| 245 | BURN_PACKAGE* pPackage; | ||
| 246 | LPWSTR sczLogPath; | ||
| 247 | DWORD dwLoggingAttributes; | ||
| 248 | INSTALLUILEVEL uiLevel; | ||
| 249 | BOOTSTRAPPER_ACTION_STATE action; | ||
| 250 | |||
| 251 | BOOTSTRAPPER_FEATURE_ACTION* rgFeatures; | ||
| 252 | BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches; | ||
| 253 | |||
| 254 | BURN_ORDERED_PATCHES* rgOrderedPatches; | ||
| 255 | DWORD cPatches; | ||
| 256 | } msiPackage; | ||
| 257 | struct | ||
| 258 | { | ||
| 259 | BURN_PACKAGE* pPackage; | ||
| 260 | LPWSTR sczTargetProductCode; | ||
| 261 | BURN_PACKAGE* pChainedTargetPackage; | ||
| 262 | BOOL fSlipstream; | ||
| 263 | BOOL fPerMachineTarget; | ||
| 264 | LPWSTR sczLogPath; | ||
| 265 | INSTALLUILEVEL uiLevel; | ||
| 266 | BOOTSTRAPPER_ACTION_STATE action; | ||
| 267 | |||
| 268 | BURN_ORDERED_PATCHES* rgOrderedPatches; | ||
| 269 | DWORD cOrderedPatches; | ||
| 270 | } mspTarget; | ||
| 271 | struct | ||
| 272 | { | ||
| 273 | BURN_PACKAGE* pPackage; | ||
| 274 | LPWSTR sczLogPath; | ||
| 275 | BOOTSTRAPPER_ACTION_STATE action; | ||
| 276 | } msuPackage; | ||
| 277 | struct | ||
| 278 | { | ||
| 279 | LPWSTR sczServiceName; | ||
| 280 | } service; | ||
| 281 | struct | ||
| 282 | { | ||
| 283 | BOOL fKeep; | ||
| 284 | } registration; | ||
| 285 | struct | ||
| 286 | { | ||
| 287 | BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; | ||
| 288 | } rollbackBoundary; | ||
| 289 | struct | ||
| 290 | { | ||
| 291 | BURN_PACKAGE* pPackage; | ||
| 292 | BURN_DEPENDENCY_ACTION action; | ||
| 293 | } packageProvider; | ||
| 294 | struct | ||
| 295 | { | ||
| 296 | BURN_PACKAGE* pPackage; | ||
| 297 | LPWSTR sczBundleProviderKey; | ||
| 298 | BURN_DEPENDENCY_ACTION action; | ||
| 299 | } packageDependency; | ||
| 300 | struct | ||
| 301 | { | ||
| 302 | BURN_PACKAGE* pReferencePackage; | ||
| 303 | LPWSTR sczInstalledProductCode; | ||
| 304 | DWORD64 qwInstalledVersion; | ||
| 305 | } compatiblePackage; | ||
| 306 | }; | ||
| 307 | } BURN_EXECUTE_ACTION; | ||
| 308 | |||
| 309 | typedef struct _BURN_CLEAN_ACTION | ||
| 310 | { | ||
| 311 | BURN_PACKAGE* pPackage; | ||
| 312 | } BURN_CLEAN_ACTION; | ||
| 313 | |||
| 314 | typedef struct _BURN_PLAN | ||
| 315 | { | ||
| 316 | BOOTSTRAPPER_ACTION action; | ||
| 317 | LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE. | ||
| 318 | LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE. | ||
| 319 | BOOL fPerMachine; | ||
| 320 | BOOL fRegister; | ||
| 321 | DWORD dwRegistrationOperations; | ||
| 322 | BOOL fKeepRegistrationDefault; | ||
| 323 | BOOL fDisallowRemoval; | ||
| 324 | |||
| 325 | DWORD64 qwCacheSizeTotal; | ||
| 326 | |||
| 327 | DWORD64 qwEstimatedSize; | ||
| 328 | |||
| 329 | DWORD cExecutePackagesTotal; | ||
| 330 | DWORD cOverallProgressTicksTotal; | ||
| 331 | |||
| 332 | BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction; | ||
| 333 | |||
| 334 | BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions; | ||
| 335 | DWORD cRegistrationActions; | ||
| 336 | |||
| 337 | BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions; | ||
| 338 | DWORD cRollbackRegistrationActions; | ||
| 339 | |||
| 340 | BURN_CACHE_ACTION* rgCacheActions; | ||
| 341 | DWORD cCacheActions; | ||
| 342 | |||
| 343 | BURN_CACHE_ACTION* rgRollbackCacheActions; | ||
| 344 | DWORD cRollbackCacheActions; | ||
| 345 | |||
| 346 | BURN_EXECUTE_ACTION* rgExecuteActions; | ||
| 347 | DWORD cExecuteActions; | ||
| 348 | |||
| 349 | BURN_EXECUTE_ACTION* rgRollbackActions; | ||
| 350 | DWORD cRollbackActions; | ||
| 351 | |||
| 352 | BURN_CLEAN_ACTION* rgCleanActions; | ||
| 353 | DWORD cCleanActions; | ||
| 354 | |||
| 355 | DEPENDENCY* rgPlannedProviders; | ||
| 356 | UINT cPlannedProviders; | ||
| 357 | |||
| 358 | BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress; | ||
| 359 | DWORD cContainerProgress; | ||
| 360 | STRINGDICT_HANDLE shContainerProgress; | ||
| 361 | |||
| 362 | BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress; | ||
| 363 | DWORD cPayloadProgress; | ||
| 364 | STRINGDICT_HANDLE shPayloadProgress; | ||
| 365 | } BURN_PLAN; | ||
| 366 | |||
| 367 | |||
| 368 | // functions | ||
| 369 | |||
| 370 | void PlanReset( | ||
| 371 | __in BURN_PLAN* pPlan, | ||
| 372 | __in BURN_PACKAGES* pPackages | ||
| 373 | ); | ||
| 374 | void PlanUninitializeExecuteAction( | ||
| 375 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
| 376 | ); | ||
| 377 | HRESULT PlanSetVariables( | ||
| 378 | __in BOOTSTRAPPER_ACTION action, | ||
| 379 | __in BURN_VARIABLES* pVariables | ||
| 380 | ); | ||
| 381 | HRESULT PlanDefaultPackageRequestState( | ||
| 382 | __in BURN_PACKAGE_TYPE packageType, | ||
| 383 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
| 384 | __in BOOL fPermanent, | ||
| 385 | __in BOOTSTRAPPER_ACTION action, | ||
| 386 | __in BURN_VARIABLES* pVariables, | ||
| 387 | __in_z_opt LPCWSTR wzInstallCondition, | ||
| 388 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 389 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
| 390 | ); | ||
| 391 | HRESULT PlanLayoutBundle( | ||
| 392 | __in BURN_PLAN* pPlan, | ||
| 393 | __in_z LPCWSTR wzExecutableName, | ||
| 394 | __in DWORD64 qwBundleSize, | ||
| 395 | __in BURN_VARIABLES* pVariables, | ||
| 396 | __in BURN_PAYLOADS* pPayloads, | ||
| 397 | __out_z LPWSTR* psczLayoutDirectory | ||
| 398 | ); | ||
| 399 | HRESULT PlanPackages( | ||
| 400 | __in BURN_REGISTRATION* pRegistration, | ||
| 401 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 402 | __in BURN_PACKAGES* pPackages, | ||
| 403 | __in BURN_PLAN* pPlan, | ||
| 404 | __in BURN_LOGGING* pLog, | ||
| 405 | __in BURN_VARIABLES* pVariables, | ||
| 406 | __in BOOL fBundleInstalled, | ||
| 407 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 408 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 409 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
| 410 | __inout HANDLE* phSyncpointEvent | ||
| 411 | ); | ||
| 412 | HRESULT PlanRegistration( | ||
| 413 | __in BURN_PLAN* pPlan, | ||
| 414 | __in BURN_REGISTRATION* pRegistration, | ||
| 415 | __in BOOTSTRAPPER_RESUME_TYPE resumeType, | ||
| 416 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 417 | __in_z_opt LPCWSTR wzIgnoreDependencies, | ||
| 418 | __out BOOL* pfContinuePlanning | ||
| 419 | ); | ||
| 420 | HRESULT PlanPassThroughBundle( | ||
| 421 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 422 | __in BURN_PACKAGE* pPackage, | ||
| 423 | __in BURN_PLAN* pPlan, | ||
| 424 | __in BURN_LOGGING* pLog, | ||
| 425 | __in BURN_VARIABLES* pVariables, | ||
| 426 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 427 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 428 | __inout HANDLE* phSyncpointEvent | ||
| 429 | ); | ||
| 430 | HRESULT PlanUpdateBundle( | ||
| 431 | __in BURN_USER_EXPERIENCE* pUX, | ||
| 432 | __in BURN_PACKAGE* pPackage, | ||
| 433 | __in BURN_PLAN* pPlan, | ||
| 434 | __in BURN_LOGGING* pLog, | ||
| 435 | __in BURN_VARIABLES* pVariables, | ||
| 436 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 437 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 438 | __inout HANDLE* phSyncpointEvent | ||
| 439 | ); | ||
| 440 | HRESULT PlanLayoutPackage( | ||
| 441 | __in BURN_PLAN* pPlan, | ||
| 442 | __in BURN_PACKAGE* pPackage, | ||
| 443 | __in_z_opt LPCWSTR wzLayoutDirectory | ||
| 444 | ); | ||
| 445 | HRESULT PlanCachePackage( | ||
| 446 | __in BOOL fPerMachine, | ||
| 447 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 448 | __in BURN_PLAN* pPlan, | ||
| 449 | __in BURN_PACKAGE* pPackage, | ||
| 450 | __in BURN_VARIABLES* pVariables, | ||
| 451 | __out HANDLE* phSyncpointEvent | ||
| 452 | ); | ||
| 453 | HRESULT PlanExecutePackage( | ||
| 454 | __in BOOL fPerMachine, | ||
| 455 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 456 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 457 | __in BURN_PLAN* pPlan, | ||
| 458 | __in BURN_PACKAGE* pPackage, | ||
| 459 | __in BURN_LOGGING* pLog, | ||
| 460 | __in BURN_VARIABLES* pVariables, | ||
| 461 | __inout HANDLE* phSyncpointEvent | ||
| 462 | ); | ||
| 463 | HRESULT PlanRelatedBundlesBegin( | ||
| 464 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 465 | __in BURN_REGISTRATION* pRegistration, | ||
| 466 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 467 | __in BURN_PLAN* pPlan | ||
| 468 | ); | ||
| 469 | HRESULT PlanRelatedBundlesComplete( | ||
| 470 | __in BURN_REGISTRATION* pRegistration, | ||
| 471 | __in BURN_PLAN* pPlan, | ||
| 472 | __in BURN_LOGGING* pLog, | ||
| 473 | __in BURN_VARIABLES* pVariables, | ||
| 474 | __inout HANDLE* phSyncpointEvent, | ||
| 475 | __in DWORD dwExecuteActionEarlyIndex | ||
| 476 | ); | ||
| 477 | HRESULT PlanFinalizeActions( | ||
| 478 | __in BURN_PLAN* pPlan | ||
| 479 | ); | ||
| 480 | HRESULT PlanCleanPackage( | ||
| 481 | __in BURN_PLAN* pPlan, | ||
| 482 | __in BURN_PACKAGE* pPackage | ||
| 483 | ); | ||
| 484 | HRESULT PlanExecuteCacheSyncAndRollback( | ||
| 485 | __in BURN_PLAN* pPlan, | ||
| 486 | __in BURN_PACKAGE* pPackage, | ||
| 487 | __in HANDLE hCacheEvent, | ||
| 488 | __in BOOL fPlanPackageCacheRollback | ||
| 489 | ); | ||
| 490 | HRESULT PlanExecuteCheckpoint( | ||
| 491 | __in BURN_PLAN* pPlan | ||
| 492 | ); | ||
| 493 | HRESULT PlanInsertExecuteAction( | ||
| 494 | __in DWORD dwIndex, | ||
| 495 | __in BURN_PLAN* pPlan, | ||
| 496 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
| 497 | ); | ||
| 498 | HRESULT PlanInsertRollbackAction( | ||
| 499 | __in DWORD dwIndex, | ||
| 500 | __in BURN_PLAN* pPlan, | ||
| 501 | __out BURN_EXECUTE_ACTION** ppRollbackAction | ||
| 502 | ); | ||
| 503 | HRESULT PlanAppendExecuteAction( | ||
| 504 | __in BURN_PLAN* pPlan, | ||
| 505 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
| 506 | ); | ||
| 507 | HRESULT PlanAppendRollbackAction( | ||
| 508 | __in BURN_PLAN* pPlan, | ||
| 509 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
| 510 | ); | ||
| 511 | HRESULT PlanKeepRegistration( | ||
| 512 | __in BURN_PLAN* pPlan, | ||
| 513 | __in DWORD iAfterExecutePackageAction, | ||
| 514 | __in DWORD iBeforeRollbackPackageAction | ||
| 515 | ); | ||
| 516 | HRESULT PlanRemoveRegistration( | ||
| 517 | __in BURN_PLAN* pPlan, | ||
| 518 | __in DWORD iAfterExecutePackageAction, | ||
| 519 | __in DWORD iAfterRollbackPackageAction | ||
| 520 | ); | ||
| 521 | HRESULT PlanRollbackBoundaryBegin( | ||
| 522 | __in BURN_PLAN* pPlan, | ||
| 523 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
| 524 | ); | ||
| 525 | HRESULT PlanRollbackBoundaryComplete( | ||
| 526 | __in BURN_PLAN* pPlan | ||
| 527 | ); | ||
| 528 | HRESULT PlanSetResumeCommand( | ||
| 529 | __in BURN_REGISTRATION* pRegistration, | ||
| 530 | __in BOOTSTRAPPER_ACTION action, | ||
| 531 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 532 | __in BURN_LOGGING* pLog | ||
| 533 | ); | ||
| 534 | |||
| 535 | #ifdef DEBUG | ||
| 536 | void PlanDump( | ||
| 537 | __in BURN_PLAN* pPlan | ||
| 538 | ); | ||
| 539 | #endif | ||
| 540 | |||
| 541 | #if defined(__cplusplus) | ||
| 542 | } | ||
| 543 | #endif | ||
diff --git a/src/engine/platform.cpp b/src/engine/platform.cpp new file mode 100644 index 00000000..9469ff49 --- /dev/null +++ b/src/engine/platform.cpp | |||
| @@ -0,0 +1,16 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // variables | ||
| 7 | |||
| 8 | PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
| 9 | |||
| 10 | |||
| 11 | // function definitions | ||
| 12 | |||
| 13 | extern "C" void PlatformInitialize() | ||
| 14 | { | ||
| 15 | vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW; | ||
| 16 | } | ||
diff --git a/src/engine/platform.h b/src/engine/platform.h new file mode 100644 index 00000000..3681f248 --- /dev/null +++ b/src/engine/platform.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // typedefs | ||
| 11 | |||
| 12 | typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)( | ||
| 13 | __in_opt LPWSTR lpMachineName, | ||
| 14 | __in_opt LPWSTR lpMessage, | ||
| 15 | __in DWORD dwTimeout, | ||
| 16 | __in BOOL bForceAppsClosed, | ||
| 17 | __in BOOL bRebootAfterShutdown, | ||
| 18 | __in DWORD dwReason | ||
| 19 | ); | ||
| 20 | |||
| 21 | |||
| 22 | // variable declarations | ||
| 23 | |||
| 24 | extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
| 25 | |||
| 26 | |||
| 27 | // function declarations | ||
| 28 | |||
| 29 | void PlatformInitialize(); | ||
| 30 | |||
| 31 | |||
| 32 | #if defined(__cplusplus) | ||
| 33 | } | ||
| 34 | #endif | ||
diff --git a/src/engine/precomp.h b/src/engine/precomp.h new file mode 100644 index 00000000..d3ebe354 --- /dev/null +++ b/src/engine/precomp.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #define ExitTrace LogErrorString | ||
| 6 | |||
| 7 | #include <wixver.h> | ||
| 8 | |||
| 9 | #include <windows.h> | ||
| 10 | #include <aclapi.h> | ||
| 11 | #include <Bits.h> | ||
| 12 | #include <gdiplus.h> | ||
| 13 | #include <math.h> | ||
| 14 | #include <msiquery.h> | ||
| 15 | #include <sddl.h> | ||
| 16 | #include <shlobj.h> | ||
| 17 | #include <shlwapi.h> | ||
| 18 | #include <softpub.h> | ||
| 19 | #include <strsafe.h> | ||
| 20 | #include <intsafe.h> | ||
| 21 | #include <mscat.h> | ||
| 22 | #include <lmcons.h> | ||
| 23 | #include <wininet.h> | ||
| 24 | #include <stddef.h> | ||
| 25 | |||
| 26 | #include <dutil.h> | ||
| 27 | #include <aclutil.h> | ||
| 28 | #include <apputil.h> | ||
| 29 | #include <buffutil.h> | ||
| 30 | #include <cabutil.h> | ||
| 31 | #include <certutil.h> | ||
| 32 | #include <cryputil.h> | ||
| 33 | #include <dirutil.h> | ||
| 34 | #include <fileutil.h> | ||
| 35 | #include <gdiputil.h> | ||
| 36 | #include <guidutil.h> | ||
| 37 | #include <logutil.h> | ||
| 38 | #include <memutil.h> | ||
| 39 | #include <osutil.h> | ||
| 40 | #include <pathutil.h> | ||
| 41 | #include <polcutil.h> | ||
| 42 | #include <procutil.h> | ||
| 43 | #include <regutil.h> | ||
| 44 | #include <resrutil.h> | ||
| 45 | #include <shelutil.h> | ||
| 46 | #include <srputil.h> | ||
| 47 | #include <strutil.h> | ||
| 48 | #include <svcutil.h> | ||
| 49 | #include <userutil.h> | ||
| 50 | #include <wiutil.h> | ||
| 51 | #include <wuautil.h> | ||
| 52 | #include <xmlutil.h> | ||
| 53 | #include <dictutil.h> | ||
| 54 | #include <deputil.h> | ||
| 55 | #include <dlutil.h> | ||
| 56 | #include <atomutil.h> | ||
| 57 | #include <apuputil.h> | ||
| 58 | |||
| 59 | #include "BootstrapperEngine.h" | ||
| 60 | #include "BootstrapperApplication.h" | ||
| 61 | |||
| 62 | #include "platform.h" | ||
| 63 | #include "variant.h" | ||
| 64 | #include "variable.h" | ||
| 65 | #include "condition.h" | ||
| 66 | #include "search.h" | ||
| 67 | #include "section.h" | ||
| 68 | #include "approvedexe.h" | ||
| 69 | #include "container.h" | ||
| 70 | #include "catalog.h" | ||
| 71 | #include "payload.h" | ||
| 72 | #include "cabextract.h" | ||
| 73 | #include "userexperience.h" | ||
| 74 | #include "package.h" | ||
| 75 | #include "update.h" | ||
| 76 | #include "pseudobundle.h" | ||
| 77 | #include "registration.h" | ||
| 78 | #include "relatedbundle.h" | ||
| 79 | #include "detect.h" | ||
| 80 | #include "plan.h" | ||
| 81 | #include "logging.h" | ||
| 82 | #include "pipe.h" | ||
| 83 | #include "core.h" | ||
| 84 | #include "cache.h" | ||
| 85 | #include "apply.h" | ||
| 86 | #include "exeengine.h" | ||
| 87 | #include "msiengine.h" | ||
| 88 | #include "mspengine.h" | ||
| 89 | #include "msuengine.h" | ||
| 90 | #include "dependency.h" | ||
| 91 | #include "elevation.h" | ||
| 92 | #include "embedded.h" | ||
| 93 | #include "manifest.h" | ||
| 94 | #include "splashscreen.h" | ||
| 95 | #include "uithread.h" | ||
| 96 | #include "bitsengine.h" | ||
| 97 | #include "netfxchainer.h" | ||
| 98 | |||
| 99 | #include "EngineForApplication.h" | ||
| 100 | #include "engine.messages.h" | ||
diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp new file mode 100644 index 00000000..ebdc040a --- /dev/null +++ b/src/engine/pseudobundle.cpp | |||
| @@ -0,0 +1,271 @@ | |||
| 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 | |||
| 5 | |||
| 6 | extern "C" HRESULT PseudoBundleInitialize( | ||
| 7 | __in DWORD64 qwEngineVersion, | ||
| 8 | __in BURN_PACKAGE* pPackage, | ||
| 9 | __in BOOL fPerMachine, | ||
| 10 | __in_z LPCWSTR wzId, | ||
| 11 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 12 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 13 | __in_z LPCWSTR wzFilePath, | ||
| 14 | __in_z LPCWSTR wzLocalSource, | ||
| 15 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 16 | __in DWORD64 qwSize, | ||
| 17 | __in BOOL fVital, | ||
| 18 | __in_z_opt LPCWSTR wzInstallArguments, | ||
| 19 | __in_z_opt LPCWSTR wzRepairArguments, | ||
| 20 | __in_z_opt LPCWSTR wzUninstallArguments, | ||
| 21 | __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, | ||
| 22 | __in_opt BYTE* pbHash, | ||
| 23 | __in DWORD cbHash | ||
| 24 | ) | ||
| 25 | { | ||
| 26 | HRESULT hr = S_OK; | ||
| 27 | LPWSTR sczRelationTypeCommandLineSwitch = NULL; | ||
| 28 | |||
| 29 | LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); | ||
| 30 | if (wzRelationTypeCommandLine) | ||
| 31 | { | ||
| 32 | hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine); | ||
| 33 | } | ||
| 34 | |||
| 35 | // Initialize the single payload, and fill out all the necessary fields | ||
| 36 | pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE); | ||
| 37 | ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct"); | ||
| 38 | pPackage->cPayloads = 1; | ||
| 39 | |||
| 40 | pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); | ||
| 41 | ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); | ||
| 42 | pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; | ||
| 43 | pPackage->rgPayloads->pPayload->qwFileSize = qwSize; | ||
| 44 | |||
| 45 | hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0); | ||
| 46 | ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); | ||
| 47 | |||
| 48 | hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0); | ||
| 49 | ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); | ||
| 50 | |||
| 51 | hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0); | ||
| 52 | ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); | ||
| 53 | |||
| 54 | if (wzDownloadSource && *wzDownloadSource) | ||
| 55 | { | ||
| 56 | hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0); | ||
| 57 | ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); | ||
| 58 | } | ||
| 59 | |||
| 60 | if (pbHash) | ||
| 61 | { | ||
| 62 | pPackage->rgPayloads->pPayload->pbHash = static_cast<BYTE*>(MemAlloc(cbHash, FALSE)); | ||
| 63 | ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); | ||
| 64 | |||
| 65 | pPackage->rgPayloads->pPayload->cbHash = cbHash; | ||
| 66 | memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); | ||
| 67 | } | ||
| 68 | |||
| 69 | pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state); | ||
| 70 | |||
| 71 | pPackage->Exe.fPseudoBundle = TRUE; | ||
| 72 | |||
| 73 | pPackage->type = BURN_PACKAGE_TYPE_EXE; | ||
| 74 | pPackage->fPerMachine = fPerMachine; | ||
| 75 | pPackage->currentState = state; | ||
| 76 | pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE; | ||
| 77 | pPackage->qwInstallSize = qwSize; | ||
| 78 | pPackage->qwSize = qwSize; | ||
| 79 | pPackage->fVital = fVital; | ||
| 80 | |||
| 81 | hr = StrAllocString(&pPackage->sczId, wzId, 0); | ||
| 82 | ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); | ||
| 83 | |||
| 84 | hr = StrAllocString(&pPackage->sczCacheId, wzId, 0); | ||
| 85 | ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle."); | ||
| 86 | |||
| 87 | // If we are a self updating bundle, we don't have to have Install arguments. | ||
| 88 | if (wzInstallArguments) | ||
| 89 | { | ||
| 90 | hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0); | ||
| 91 | ExitOnFailure(hr, "Failed to copy install arguments for related bundle package"); | ||
| 92 | } | ||
| 93 | |||
| 94 | if (sczRelationTypeCommandLineSwitch) | ||
| 95 | { | ||
| 96 | hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0); | ||
| 97 | ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package"); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (wzRepairArguments) | ||
| 101 | { | ||
| 102 | hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0); | ||
| 103 | ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package"); | ||
| 104 | |||
| 105 | if (sczRelationTypeCommandLineSwitch) | ||
| 106 | { | ||
| 107 | hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0); | ||
| 108 | ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package"); | ||
| 109 | } | ||
| 110 | |||
| 111 | pPackage->Exe.fRepairable = TRUE; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (wzUninstallArguments) | ||
| 115 | { | ||
| 116 | hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0); | ||
| 117 | ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package"); | ||
| 118 | |||
| 119 | if (sczRelationTypeCommandLineSwitch) | ||
| 120 | { | ||
| 121 | hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0); | ||
| 122 | ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package"); | ||
| 123 | } | ||
| 124 | |||
| 125 | pPackage->fUninstallable = TRUE; | ||
| 126 | } | ||
| 127 | |||
| 128 | // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). | ||
| 129 | pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; | ||
| 130 | |||
| 131 | // All versions of Burn past v3.9 RTM support suppressing ancestors. | ||
| 132 | pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion; | ||
| 133 | |||
| 134 | if (pDependencyProvider) | ||
| 135 | { | ||
| 136 | pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); | ||
| 137 | ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); | ||
| 138 | pPackage->cDependencyProviders = 1; | ||
| 139 | |||
| 140 | pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; | ||
| 141 | |||
| 142 | hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); | ||
| 143 | ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); | ||
| 144 | |||
| 145 | hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); | ||
| 146 | ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); | ||
| 147 | |||
| 148 | hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); | ||
| 149 | ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); | ||
| 150 | } | ||
| 151 | |||
| 152 | LExit: | ||
| 153 | ReleaseStr(sczRelationTypeCommandLineSwitch); | ||
| 154 | |||
| 155 | return hr; | ||
| 156 | } | ||
| 157 | |||
| 158 | extern "C" HRESULT PseudoBundleInitializePassthrough( | ||
| 159 | __in BURN_PACKAGE* pPassthroughPackage, | ||
| 160 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 161 | __in_z_opt LPCWSTR wzAppendLogPath, | ||
| 162 | __in_z_opt LPWSTR wzActiveParent, | ||
| 163 | __in_z_opt LPWSTR wzAncestors, | ||
| 164 | __in BURN_PACKAGE* pPackage | ||
| 165 | ) | ||
| 166 | { | ||
| 167 | Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type); | ||
| 168 | |||
| 169 | HRESULT hr = S_OK; | ||
| 170 | LPWSTR sczArguments = NULL; | ||
| 171 | |||
| 172 | // Initialize the payloads, and copy the necessary fields. | ||
| 173 | pPassthroughPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * pPackage->cPayloads, TRUE); | ||
| 174 | ExitOnNull(pPassthroughPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle."); | ||
| 175 | pPassthroughPackage->cPayloads = pPackage->cPayloads; | ||
| 176 | |||
| 177 | for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) | ||
| 178 | { | ||
| 179 | BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; | ||
| 180 | |||
| 181 | pPassthroughPackage->rgPayloads[iPayload].pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); | ||
| 182 | ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); | ||
| 183 | pPassthroughPackage->rgPayloads[iPayload].pPayload->packaging = pPayload->pPayload->packaging; | ||
| 184 | pPassthroughPackage->rgPayloads[iPayload].pPayload->qwFileSize = pPayload->pPayload->qwFileSize; | ||
| 185 | |||
| 186 | hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczKey, pPayload->pPayload->sczKey, 0); | ||
| 187 | ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle payload."); | ||
| 188 | |||
| 189 | hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczFilePath, pPayload->pPayload->sczFilePath, 0); | ||
| 190 | ExitOnFailure(hr, "Failed to copy filename for passthrough pseudo bundle."); | ||
| 191 | |||
| 192 | hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczSourcePath, pPayload->pPayload->sczSourcePath, 0); | ||
| 193 | ExitOnFailure(hr, "Failed to copy local source path for passthrough pseudo bundle."); | ||
| 194 | |||
| 195 | if (pPayload->pPayload->downloadSource.sczUrl) | ||
| 196 | { | ||
| 197 | hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->downloadSource.sczUrl, pPayload->pPayload->downloadSource.sczUrl, 0); | ||
| 198 | ExitOnFailure(hr, "Failed to copy download source for passthrough pseudo bundle."); | ||
| 199 | } | ||
| 200 | |||
| 201 | if (pPayload->pPayload->pbHash) | ||
| 202 | { | ||
| 203 | pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash = static_cast<BYTE*>(MemAlloc(pPayload->pPayload->cbHash, FALSE)); | ||
| 204 | ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); | ||
| 205 | |||
| 206 | pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash = pPayload->pPayload->cbHash; | ||
| 207 | memcpy_s(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash, pPayload->pPayload->pbHash, pPayload->pPayload->cbHash); | ||
| 208 | } | ||
| 209 | |||
| 210 | pPassthroughPackage->rgPayloads[iPayload].fCached = pPayload->fCached; | ||
| 211 | } | ||
| 212 | |||
| 213 | pPassthroughPackage->Exe.fPseudoBundle = TRUE; | ||
| 214 | |||
| 215 | pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user. | ||
| 216 | pPassthroughPackage->type = pPackage->type; | ||
| 217 | pPassthroughPackage->currentState = pPackage->currentState; | ||
| 218 | pPassthroughPackage->cache = pPackage->cache; | ||
| 219 | pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize; | ||
| 220 | pPassthroughPackage->qwSize = pPackage->qwSize; | ||
| 221 | pPassthroughPackage->fVital = pPackage->fVital; | ||
| 222 | |||
| 223 | hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0); | ||
| 224 | ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle."); | ||
| 225 | |||
| 226 | hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0); | ||
| 227 | ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle."); | ||
| 228 | |||
| 229 | pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol; | ||
| 230 | |||
| 231 | // No matter the operation, we're passing the same command-line. That's what makes | ||
| 232 | // this a passthrough bundle. | ||
| 233 | hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine); | ||
| 234 | ExitOnFailure(hr, "Failed to recreate command-line arguments."); | ||
| 235 | |||
| 236 | hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0); | ||
| 237 | ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package"); | ||
| 238 | |||
| 239 | hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0); | ||
| 240 | ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package"); | ||
| 241 | |||
| 242 | pPassthroughPackage->Exe.fRepairable = TRUE; | ||
| 243 | |||
| 244 | hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0); | ||
| 245 | ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package"); | ||
| 246 | |||
| 247 | pPassthroughPackage->fUninstallable = TRUE; | ||
| 248 | |||
| 249 | // TODO: consider bringing this back in the near future. | ||
| 250 | //if (pDependencyProvider) | ||
| 251 | //{ | ||
| 252 | // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); | ||
| 253 | // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); | ||
| 254 | // pPassthroughPackage->cDependencyProviders = 1; | ||
| 255 | |||
| 256 | // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; | ||
| 257 | |||
| 258 | // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); | ||
| 259 | // ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); | ||
| 260 | |||
| 261 | // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); | ||
| 262 | // ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); | ||
| 263 | |||
| 264 | // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); | ||
| 265 | // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); | ||
| 266 | //} | ||
| 267 | |||
| 268 | LExit: | ||
| 269 | ReleaseStr(sczArguments); | ||
| 270 | return hr; | ||
| 271 | } | ||
diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h new file mode 100644 index 00000000..144f6880 --- /dev/null +++ b/src/engine/pseudobundle.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | HRESULT PseudoBundleInitialize( | ||
| 10 | __in DWORD64 qwEngineVersion, | ||
| 11 | __in BURN_PACKAGE* pPackage, | ||
| 12 | __in BOOL fPerMachine, | ||
| 13 | __in_z LPCWSTR wzId, | ||
| 14 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 15 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 16 | __in_z LPCWSTR wzFilePath, | ||
| 17 | __in_z LPCWSTR wzLocalSource, | ||
| 18 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 19 | __in DWORD64 qwSize, | ||
| 20 | __in BOOL fVital, | ||
| 21 | __in_z_opt LPCWSTR wzInstallArguments, | ||
| 22 | __in_z_opt LPCWSTR wzRepairArguments, | ||
| 23 | __in_z_opt LPCWSTR wzUninstallArguments, | ||
| 24 | __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, | ||
| 25 | __in_opt BYTE* pbHash, | ||
| 26 | __in DWORD cbHash | ||
| 27 | ); | ||
| 28 | HRESULT PseudoBundleInitializePassthrough( | ||
| 29 | __in BURN_PACKAGE* pPassthroughPackage, | ||
| 30 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
| 31 | __in_z_opt LPCWSTR wzAppendLogPath, | ||
| 32 | __in_z_opt LPWSTR wzActiveParent, | ||
| 33 | __in_z_opt LPWSTR wzAncestors, | ||
| 34 | __in BURN_PACKAGE* pPackage | ||
| 35 | ); | ||
| 36 | |||
| 37 | #if defined(__cplusplus) | ||
| 38 | } | ||
| 39 | #endif | ||
diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp new file mode 100644 index 00000000..93c990f5 --- /dev/null +++ b/src/engine/registration.cpp | |||
| @@ -0,0 +1,1599 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; | ||
| 9 | const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; | ||
| 10 | const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; | ||
| 11 | const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; | ||
| 12 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; | ||
| 13 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; | ||
| 14 | const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; | ||
| 15 | const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; | ||
| 16 | const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; | ||
| 17 | const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; | ||
| 18 | const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; | ||
| 19 | const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; | ||
| 20 | const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; | ||
| 21 | const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; | ||
| 22 | const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; | ||
| 23 | const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; | ||
| 24 | const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; | ||
| 25 | const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; | ||
| 26 | const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; | ||
| 27 | const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; | ||
| 28 | const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; | ||
| 29 | const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; | ||
| 30 | const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; | ||
| 31 | const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; | ||
| 32 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; | ||
| 33 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; | ||
| 34 | |||
| 35 | // internal function declarations | ||
| 36 | |||
| 37 | static HRESULT ParseSoftwareTagsFromXml( | ||
| 38 | __in IXMLDOMNode* pixnRegistrationNode, | ||
| 39 | __out BURN_SOFTWARE_TAG** prgSoftwareTags, | ||
| 40 | __out DWORD* pcSoftwareTags | ||
| 41 | ); | ||
| 42 | static HRESULT SetPaths( | ||
| 43 | __in BURN_REGISTRATION* pRegistration | ||
| 44 | ); | ||
| 45 | static HRESULT GetBundleManufacturer( | ||
| 46 | __in BURN_REGISTRATION* pRegistration, | ||
| 47 | __in BURN_VARIABLES* pVariables, | ||
| 48 | __out LPWSTR* psczBundleManufacturer | ||
| 49 | ); | ||
| 50 | static HRESULT GetBundleName( | ||
| 51 | __in BURN_REGISTRATION* pRegistration, | ||
| 52 | __in BURN_VARIABLES* pVariables, | ||
| 53 | __out LPWSTR* psczBundleName | ||
| 54 | ); | ||
| 55 | static HRESULT UpdateResumeMode( | ||
| 56 | __in BURN_REGISTRATION* pRegistration, | ||
| 57 | __in HKEY hkRegistration, | ||
| 58 | __in BURN_RESUME_MODE resumeMode, | ||
| 59 | __in BOOL fRestartInitiated | ||
| 60 | ); | ||
| 61 | static HRESULT ParseRelatedCodes( | ||
| 62 | __in BURN_REGISTRATION* pRegistration, | ||
| 63 | __in IXMLDOMNode* pixnBundle | ||
| 64 | ); | ||
| 65 | static HRESULT FormatUpdateRegistrationKey( | ||
| 66 | __in BURN_REGISTRATION* pRegistration, | ||
| 67 | __out_z LPWSTR* psczKey | ||
| 68 | ); | ||
| 69 | static HRESULT WriteSoftwareTags( | ||
| 70 | __in BOOL fPerMachine, | ||
| 71 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
| 72 | ); | ||
| 73 | static HRESULT RemoveSoftwareTags( | ||
| 74 | __in BOOL fPerMachine, | ||
| 75 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
| 76 | ); | ||
| 77 | static HRESULT WriteUpdateRegistration( | ||
| 78 | __in BURN_REGISTRATION* pRegistration, | ||
| 79 | __in BURN_VARIABLES* pVariables | ||
| 80 | ); | ||
| 81 | static HRESULT RemoveUpdateRegistration( | ||
| 82 | __in BURN_REGISTRATION* pRegistration | ||
| 83 | ); | ||
| 84 | static HRESULT RegWriteStringVariable( | ||
| 85 | __in HKEY hkKey, | ||
| 86 | __in BURN_VARIABLES* pVariables, | ||
| 87 | __in LPCWSTR wzVariable, | ||
| 88 | __in LPCWSTR wzName | ||
| 89 | ); | ||
| 90 | static HRESULT UpdateBundleNameRegistration( | ||
| 91 | __in BURN_REGISTRATION* pRegistration, | ||
| 92 | __in BURN_VARIABLES* pVariables, | ||
| 93 | __in HKEY hkRegistration | ||
| 94 | ); | ||
| 95 | |||
| 96 | // function definitions | ||
| 97 | |||
| 98 | /******************************************************************* | ||
| 99 | RegistrationParseFromXml - Parses registration information from manifest. | ||
| 100 | |||
| 101 | *******************************************************************/ | ||
| 102 | extern "C" HRESULT RegistrationParseFromXml( | ||
| 103 | __in BURN_REGISTRATION* pRegistration, | ||
| 104 | __in IXMLDOMNode* pixnBundle | ||
| 105 | ) | ||
| 106 | { | ||
| 107 | HRESULT hr = S_OK; | ||
| 108 | IXMLDOMNode* pixnRegistrationNode = NULL; | ||
| 109 | IXMLDOMNode* pixnArpNode = NULL; | ||
| 110 | IXMLDOMNode* pixnUpdateNode = NULL; | ||
| 111 | LPWSTR scz = NULL; | ||
| 112 | |||
| 113 | // select registration node | ||
| 114 | hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode); | ||
| 115 | if (S_FALSE == hr) | ||
| 116 | { | ||
| 117 | hr = E_NOTFOUND; | ||
| 118 | } | ||
| 119 | ExitOnFailure(hr, "Failed to select registration node."); | ||
| 120 | |||
| 121 | // @Id | ||
| 122 | hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId); | ||
| 123 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 124 | |||
| 125 | // @Tag | ||
| 126 | hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); | ||
| 127 | ExitOnFailure(hr, "Failed to get @Tag."); | ||
| 128 | |||
| 129 | hr = ParseRelatedCodes(pRegistration, pixnBundle); | ||
| 130 | ExitOnFailure(hr, "Failed to parse related bundles"); | ||
| 131 | |||
| 132 | // @Version | ||
| 133 | hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); | ||
| 134 | ExitOnFailure(hr, "Failed to get @Version."); | ||
| 135 | |||
| 136 | hr = FileVersionFromStringEx(scz, 0, &pRegistration->qwVersion); | ||
| 137 | ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); | ||
| 138 | |||
| 139 | // @ProviderKey | ||
| 140 | hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); | ||
| 141 | ExitOnFailure(hr, "Failed to get @ProviderKey."); | ||
| 142 | |||
| 143 | // @ExecutableName | ||
| 144 | hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName); | ||
| 145 | ExitOnFailure(hr, "Failed to get @ExecutableName."); | ||
| 146 | |||
| 147 | // @PerMachine | ||
| 148 | hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine); | ||
| 149 | ExitOnFailure(hr, "Failed to get @PerMachine."); | ||
| 150 | |||
| 151 | // select ARP node | ||
| 152 | hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode); | ||
| 153 | if (S_FALSE != hr) | ||
| 154 | { | ||
| 155 | ExitOnFailure(hr, "Failed to select ARP node."); | ||
| 156 | |||
| 157 | // @Register | ||
| 158 | hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp); | ||
| 159 | ExitOnFailure(hr, "Failed to get @Register."); | ||
| 160 | |||
| 161 | // @DisplayName | ||
| 162 | hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName); | ||
| 163 | if (E_NOTFOUND != hr) | ||
| 164 | { | ||
| 165 | ExitOnFailure(hr, "Failed to get @DisplayName."); | ||
| 166 | } | ||
| 167 | |||
| 168 | // @DisplayVersion | ||
| 169 | hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion); | ||
| 170 | if (E_NOTFOUND != hr) | ||
| 171 | { | ||
| 172 | ExitOnFailure(hr, "Failed to get @DisplayVersion."); | ||
| 173 | } | ||
| 174 | |||
| 175 | // @Publisher | ||
| 176 | hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher); | ||
| 177 | if (E_NOTFOUND != hr) | ||
| 178 | { | ||
| 179 | ExitOnFailure(hr, "Failed to get @Publisher."); | ||
| 180 | } | ||
| 181 | |||
| 182 | // @HelpLink | ||
| 183 | hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink); | ||
| 184 | if (E_NOTFOUND != hr) | ||
| 185 | { | ||
| 186 | ExitOnFailure(hr, "Failed to get @HelpLink."); | ||
| 187 | } | ||
| 188 | |||
| 189 | // @HelpTelephone | ||
| 190 | hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone); | ||
| 191 | if (E_NOTFOUND != hr) | ||
| 192 | { | ||
| 193 | ExitOnFailure(hr, "Failed to get @HelpTelephone."); | ||
| 194 | } | ||
| 195 | |||
| 196 | // @AboutUrl | ||
| 197 | hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl); | ||
| 198 | if (E_NOTFOUND != hr) | ||
| 199 | { | ||
| 200 | ExitOnFailure(hr, "Failed to get @AboutUrl."); | ||
| 201 | } | ||
| 202 | |||
| 203 | // @UpdateUrl | ||
| 204 | hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl); | ||
| 205 | if (E_NOTFOUND != hr) | ||
| 206 | { | ||
| 207 | ExitOnFailure(hr, "Failed to get @UpdateUrl."); | ||
| 208 | } | ||
| 209 | |||
| 210 | // @ParentDisplayName | ||
| 211 | hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName); | ||
| 212 | if (E_NOTFOUND != hr) | ||
| 213 | { | ||
| 214 | ExitOnFailure(hr, "Failed to get @ParentDisplayName."); | ||
| 215 | } | ||
| 216 | |||
| 217 | // @Comments | ||
| 218 | hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments); | ||
| 219 | if (E_NOTFOUND != hr) | ||
| 220 | { | ||
| 221 | ExitOnFailure(hr, "Failed to get @Comments."); | ||
| 222 | } | ||
| 223 | |||
| 224 | // @Contact | ||
| 225 | hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact); | ||
| 226 | if (E_NOTFOUND != hr) | ||
| 227 | { | ||
| 228 | ExitOnFailure(hr, "Failed to get @Contact."); | ||
| 229 | } | ||
| 230 | |||
| 231 | // @DisableModify | ||
| 232 | hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz); | ||
| 233 | if (SUCCEEDED(hr)) | ||
| 234 | { | ||
| 235 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1)) | ||
| 236 | { | ||
| 237 | pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON; | ||
| 238 | } | ||
| 239 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) | ||
| 240 | { | ||
| 241 | pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE; | ||
| 242 | } | ||
| 243 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) | ||
| 244 | { | ||
| 245 | pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; | ||
| 246 | } | ||
| 247 | else | ||
| 248 | { | ||
| 249 | hr = E_UNEXPECTED; | ||
| 250 | ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | else if (E_NOTFOUND == hr) | ||
| 254 | { | ||
| 255 | pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; | ||
| 256 | hr = S_OK; | ||
| 257 | } | ||
| 258 | ExitOnFailure(hr, "Failed to get @DisableModify."); | ||
| 259 | |||
| 260 | // @DisableRemove | ||
| 261 | hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove); | ||
| 262 | if (E_NOTFOUND != hr) | ||
| 263 | { | ||
| 264 | ExitOnFailure(hr, "Failed to get @DisableRemove."); | ||
| 265 | pRegistration->fNoRemoveDefined = TRUE; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags); | ||
| 270 | ExitOnFailure(hr, "Failed to parse software tag."); | ||
| 271 | |||
| 272 | // select Update node | ||
| 273 | hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode); | ||
| 274 | if (S_FALSE != hr) | ||
| 275 | { | ||
| 276 | ExitOnFailure(hr, "Failed to select Update node."); | ||
| 277 | |||
| 278 | pRegistration->update.fRegisterUpdate = TRUE; | ||
| 279 | |||
| 280 | // @Manufacturer | ||
| 281 | hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer); | ||
| 282 | ExitOnFailure(hr, "Failed to get @Manufacturer."); | ||
| 283 | |||
| 284 | // @Department | ||
| 285 | hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment); | ||
| 286 | if (E_NOTFOUND != hr) | ||
| 287 | { | ||
| 288 | ExitOnFailure(hr, "Failed to get @Department."); | ||
| 289 | } | ||
| 290 | |||
| 291 | // @ProductFamily | ||
| 292 | hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily); | ||
| 293 | if (E_NOTFOUND != hr) | ||
| 294 | { | ||
| 295 | ExitOnFailure(hr, "Failed to get @ProductFamily."); | ||
| 296 | } | ||
| 297 | |||
| 298 | // @Name | ||
| 299 | hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName); | ||
| 300 | ExitOnFailure(hr, "Failed to get @Name."); | ||
| 301 | |||
| 302 | // @Classification | ||
| 303 | hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification); | ||
| 304 | ExitOnFailure(hr, "Failed to get @Classification."); | ||
| 305 | } | ||
| 306 | |||
| 307 | hr = SetPaths(pRegistration); | ||
| 308 | ExitOnFailure(hr, "Failed to set registration paths."); | ||
| 309 | |||
| 310 | LExit: | ||
| 311 | ReleaseObject(pixnRegistrationNode); | ||
| 312 | ReleaseObject(pixnArpNode); | ||
| 313 | ReleaseObject(pixnUpdateNode); | ||
| 314 | ReleaseStr(scz); | ||
| 315 | |||
| 316 | return hr; | ||
| 317 | } | ||
| 318 | |||
| 319 | /******************************************************************* | ||
| 320 | RegistrationUninitialize - | ||
| 321 | |||
| 322 | *******************************************************************/ | ||
| 323 | extern "C" void RegistrationUninitialize( | ||
| 324 | __in BURN_REGISTRATION* pRegistration | ||
| 325 | ) | ||
| 326 | { | ||
| 327 | ReleaseStr(pRegistration->sczId); | ||
| 328 | ReleaseStr(pRegistration->sczTag); | ||
| 329 | |||
| 330 | for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) | ||
| 331 | { | ||
| 332 | ReleaseStr(pRegistration->rgsczDetectCodes[i]); | ||
| 333 | } | ||
| 334 | ReleaseMem(pRegistration->rgsczDetectCodes); | ||
| 335 | |||
| 336 | for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i) | ||
| 337 | { | ||
| 338 | ReleaseStr(pRegistration->rgsczUpgradeCodes[i]); | ||
| 339 | } | ||
| 340 | ReleaseMem(pRegistration->rgsczUpgradeCodes); | ||
| 341 | |||
| 342 | for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i) | ||
| 343 | { | ||
| 344 | ReleaseStr(pRegistration->rgsczAddonCodes[i]); | ||
| 345 | } | ||
| 346 | ReleaseMem(pRegistration->rgsczAddonCodes); | ||
| 347 | |||
| 348 | for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i) | ||
| 349 | { | ||
| 350 | ReleaseStr(pRegistration->rgsczPatchCodes[i]); | ||
| 351 | } | ||
| 352 | ReleaseMem(pRegistration->rgsczPatchCodes); | ||
| 353 | |||
| 354 | ReleaseStr(pRegistration->sczProviderKey); | ||
| 355 | ReleaseStr(pRegistration->sczActiveParent); | ||
| 356 | ReleaseStr(pRegistration->sczExecutableName); | ||
| 357 | |||
| 358 | ReleaseStr(pRegistration->sczRegistrationKey); | ||
| 359 | ReleaseStr(pRegistration->sczCacheExecutablePath); | ||
| 360 | ReleaseStr(pRegistration->sczResumeCommandLine); | ||
| 361 | ReleaseStr(pRegistration->sczStateFile); | ||
| 362 | |||
| 363 | ReleaseStr(pRegistration->sczDisplayName); | ||
| 364 | ReleaseStr(pRegistration->sczDisplayVersion); | ||
| 365 | ReleaseStr(pRegistration->sczPublisher); | ||
| 366 | ReleaseStr(pRegistration->sczHelpLink); | ||
| 367 | ReleaseStr(pRegistration->sczHelpTelephone); | ||
| 368 | ReleaseStr(pRegistration->sczAboutUrl); | ||
| 369 | ReleaseStr(pRegistration->sczUpdateUrl); | ||
| 370 | ReleaseStr(pRegistration->sczParentDisplayName); | ||
| 371 | ReleaseStr(pRegistration->sczComments); | ||
| 372 | ReleaseStr(pRegistration->sczContact); | ||
| 373 | |||
| 374 | ReleaseStr(pRegistration->update.sczManufacturer); | ||
| 375 | ReleaseStr(pRegistration->update.sczDepartment); | ||
| 376 | ReleaseStr(pRegistration->update.sczProductFamily); | ||
| 377 | ReleaseStr(pRegistration->update.sczName); | ||
| 378 | ReleaseStr(pRegistration->update.sczClassification); | ||
| 379 | |||
| 380 | if (pRegistration->softwareTags.rgSoftwareTags) | ||
| 381 | { | ||
| 382 | for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i) | ||
| 383 | { | ||
| 384 | ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); | ||
| 385 | ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); | ||
| 386 | ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); | ||
| 387 | } | ||
| 388 | |||
| 389 | MemFree(pRegistration->softwareTags.rgSoftwareTags); | ||
| 390 | } | ||
| 391 | |||
| 392 | ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); | ||
| 393 | ReleaseStr(pRegistration->sczAncestors); | ||
| 394 | RelatedBundlesUninitialize(&pRegistration->relatedBundles); | ||
| 395 | |||
| 396 | // clear struct | ||
| 397 | memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); | ||
| 398 | } | ||
| 399 | |||
| 400 | /******************************************************************* | ||
| 401 | RegistrationSetVariables - Initializes bundle variables that map to | ||
| 402 | registration entities. | ||
| 403 | |||
| 404 | *******************************************************************/ | ||
| 405 | extern "C" HRESULT RegistrationSetVariables( | ||
| 406 | __in BURN_REGISTRATION* pRegistration, | ||
| 407 | __in BURN_VARIABLES* pVariables | ||
| 408 | ) | ||
| 409 | { | ||
| 410 | HRESULT hr = S_OK; | ||
| 411 | LPWSTR sczBundleManufacturer = NULL; | ||
| 412 | LPWSTR sczBundleName = NULL; | ||
| 413 | |||
| 414 | if (pRegistration->fInstalled) | ||
| 415 | { | ||
| 416 | hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE); | ||
| 417 | ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); | ||
| 418 | } | ||
| 419 | |||
| 420 | // Ensure the registration bundle name is updated. | ||
| 421 | hr = GetBundleName(pRegistration, pVariables, &sczBundleName); | ||
| 422 | ExitOnFailure(hr, "Failed to initialize bundle name."); | ||
| 423 | |||
| 424 | hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName); | ||
| 425 | ExitOnFailure(hr, "Failed to initialize bundle manufacturer."); | ||
| 426 | |||
| 427 | if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) | ||
| 428 | { | ||
| 429 | hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE); | ||
| 430 | ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable."); | ||
| 431 | } | ||
| 432 | |||
| 433 | hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE); | ||
| 434 | ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); | ||
| 435 | |||
| 436 | hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE); | ||
| 437 | ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); | ||
| 438 | |||
| 439 | hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE); | ||
| 440 | ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); | ||
| 441 | |||
| 442 | LExit: | ||
| 443 | ReleaseStr(sczBundleManufacturer); | ||
| 444 | ReleaseStr(sczBundleName); | ||
| 445 | |||
| 446 | return hr; | ||
| 447 | } | ||
| 448 | |||
| 449 | extern "C" HRESULT RegistrationDetectInstalled( | ||
| 450 | __in BURN_REGISTRATION* pRegistration, | ||
| 451 | __out BOOL* pfInstalled | ||
| 452 | ) | ||
| 453 | { | ||
| 454 | HRESULT hr = S_OK; | ||
| 455 | HKEY hkRegistration = NULL; | ||
| 456 | DWORD dwInstalled = 0; | ||
| 457 | |||
| 458 | // open registration key | ||
| 459 | hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); | ||
| 460 | if (SUCCEEDED(hr)) | ||
| 461 | { | ||
| 462 | hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); | ||
| 463 | } | ||
| 464 | |||
| 465 | // Not finding the key or value is okay. | ||
| 466 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 467 | { | ||
| 468 | hr = S_OK; | ||
| 469 | } | ||
| 470 | |||
| 471 | *pfInstalled = (1 == dwInstalled); | ||
| 472 | |||
| 473 | ReleaseRegKey(hkRegistration); | ||
| 474 | return hr; | ||
| 475 | } | ||
| 476 | |||
| 477 | /******************************************************************* | ||
| 478 | RegistrationDetectResumeMode - Detects registration information on the system | ||
| 479 | to determine if a resume is taking place. | ||
| 480 | |||
| 481 | *******************************************************************/ | ||
| 482 | extern "C" HRESULT RegistrationDetectResumeType( | ||
| 483 | __in BURN_REGISTRATION* pRegistration, | ||
| 484 | __out BOOTSTRAPPER_RESUME_TYPE* pResumeType | ||
| 485 | ) | ||
| 486 | { | ||
| 487 | HRESULT hr = S_OK; | ||
| 488 | LPWSTR sczRebootRequiredKey = NULL; | ||
| 489 | HKEY hkRebootRequired = NULL; | ||
| 490 | HKEY hkRegistration = NULL; | ||
| 491 | DWORD dwResume = 0; | ||
| 492 | |||
| 493 | // Check to see if a restart is pending for this bundle. | ||
| 494 | hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); | ||
| 495 | ExitOnFailure(hr, "Failed to format pending restart registry key to read."); | ||
| 496 | |||
| 497 | hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); | ||
| 498 | if (SUCCEEDED(hr)) | ||
| 499 | { | ||
| 500 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; | ||
| 501 | ExitFunction1(hr = S_OK); | ||
| 502 | } | ||
| 503 | |||
| 504 | // open registration key | ||
| 505 | hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); | ||
| 506 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 507 | { | ||
| 508 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; | ||
| 509 | ExitFunction1(hr = S_OK); | ||
| 510 | } | ||
| 511 | ExitOnFailure(hr, "Failed to open registration key."); | ||
| 512 | |||
| 513 | // read Resume value | ||
| 514 | hr = RegReadNumber(hkRegistration, L"Resume", &dwResume); | ||
| 515 | if (E_FILENOTFOUND == hr) | ||
| 516 | { | ||
| 517 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; | ||
| 518 | ExitFunction1(hr = S_OK); | ||
| 519 | } | ||
| 520 | ExitOnFailure(hr, "Failed to read Resume value."); | ||
| 521 | |||
| 522 | switch (dwResume) | ||
| 523 | { | ||
| 524 | case BURN_RESUME_MODE_ACTIVE: | ||
| 525 | // a previous run was interrupted | ||
| 526 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED; | ||
| 527 | break; | ||
| 528 | |||
| 529 | case BURN_RESUME_MODE_SUSPEND: | ||
| 530 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND; | ||
| 531 | break; | ||
| 532 | |||
| 533 | case BURN_RESUME_MODE_ARP: | ||
| 534 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP; | ||
| 535 | break; | ||
| 536 | |||
| 537 | case BURN_RESUME_MODE_REBOOT_PENDING: | ||
| 538 | // The volatile pending registry doesn't exist (checked above) which means | ||
| 539 | // the system was successfully restarted. | ||
| 540 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT; | ||
| 541 | break; | ||
| 542 | |||
| 543 | default: | ||
| 544 | // the value stored in the registry is not valid | ||
| 545 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; | ||
| 546 | break; | ||
| 547 | } | ||
| 548 | |||
| 549 | LExit: | ||
| 550 | ReleaseRegKey(hkRegistration); | ||
| 551 | ReleaseRegKey(hkRebootRequired); | ||
| 552 | ReleaseStr(sczRebootRequiredKey); | ||
| 553 | |||
| 554 | return hr; | ||
| 555 | } | ||
| 556 | |||
| 557 | /******************************************************************* | ||
| 558 | RegistrationDetectRelatedBundles - finds the bundles with same | ||
| 559 | upgrade/detect/addon/patch codes. | ||
| 560 | |||
| 561 | *******************************************************************/ | ||
| 562 | extern "C" HRESULT RegistrationDetectRelatedBundles( | ||
| 563 | __in BURN_REGISTRATION* pRegistration | ||
| 564 | ) | ||
| 565 | { | ||
| 566 | HRESULT hr = S_OK; | ||
| 567 | |||
| 568 | hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); | ||
| 569 | ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); | ||
| 570 | |||
| 571 | hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles); | ||
| 572 | ExitOnFailure(hr, "Failed to initialize per-user related bundles."); | ||
| 573 | |||
| 574 | LExit: | ||
| 575 | return hr; | ||
| 576 | } | ||
| 577 | |||
| 578 | /******************************************************************* | ||
| 579 | RegistrationSessionBegin - Registers a run session on the system. | ||
| 580 | |||
| 581 | *******************************************************************/ | ||
| 582 | extern "C" HRESULT RegistrationSessionBegin( | ||
| 583 | __in_z LPCWSTR wzEngineWorkingPath, | ||
| 584 | __in BURN_REGISTRATION* pRegistration, | ||
| 585 | __in BURN_VARIABLES* pVariables, | ||
| 586 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 587 | __in DWORD dwRegistrationOptions, | ||
| 588 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, | ||
| 589 | __in DWORD64 qwEstimatedSize | ||
| 590 | ) | ||
| 591 | { | ||
| 592 | HRESULT hr = S_OK; | ||
| 593 | DWORD dwSize = 0; | ||
| 594 | HKEY hkRegistration = NULL; | ||
| 595 | LPWSTR sczPublisher = NULL; | ||
| 596 | |||
| 597 | LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume)); | ||
| 598 | |||
| 599 | // Cache bundle executable. | ||
| 600 | if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) | ||
| 601 | { | ||
| 602 | hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, &pUserExperience->payloads, wzEngineWorkingPath | ||
| 603 | #ifdef DEBUG | ||
| 604 | , pRegistration->sczCacheExecutablePath | ||
| 605 | #endif | ||
| 606 | ); | ||
| 607 | ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath); | ||
| 608 | } | ||
| 609 | |||
| 610 | // create registration key | ||
| 611 | hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); | ||
| 612 | ExitOnFailure(hr, "Failed to create registration key."); | ||
| 613 | |||
| 614 | // Write any ARP values and software tags. | ||
| 615 | if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION) | ||
| 616 | { | ||
| 617 | // Upgrade information | ||
| 618 | hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath); | ||
| 619 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); | ||
| 620 | |||
| 621 | hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes); | ||
| 622 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); | ||
| 623 | |||
| 624 | hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes); | ||
| 625 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE); | ||
| 626 | |||
| 627 | hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes); | ||
| 628 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE); | ||
| 629 | |||
| 630 | hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); | ||
| 631 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); | ||
| 632 | |||
| 633 | hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, L"%hu.%hu.%hu.%hu", | ||
| 634 | static_cast<WORD>(pRegistration->qwVersion >> 48), static_cast<WORD>(pRegistration->qwVersion >> 32), | ||
| 635 | static_cast<WORD>(pRegistration->qwVersion >> 16), static_cast<WORD>(pRegistration->qwVersion)); | ||
| 636 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); | ||
| 637 | |||
| 638 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, static_cast<WORD>(pRegistration->qwVersion >> 48)); | ||
| 639 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); | ||
| 640 | |||
| 641 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, static_cast<WORD>(pRegistration->qwVersion >> 32)); | ||
| 642 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); | ||
| 643 | |||
| 644 | if (pRegistration->sczProviderKey) | ||
| 645 | { | ||
| 646 | hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey); | ||
| 647 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY); | ||
| 648 | } | ||
| 649 | |||
| 650 | if (pRegistration->sczTag) | ||
| 651 | { | ||
| 652 | hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag); | ||
| 653 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG); | ||
| 654 | } | ||
| 655 | |||
| 656 | hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild); | ||
| 657 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION); | ||
| 658 | |||
| 659 | // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable. | ||
| 660 | hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); | ||
| 661 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); | ||
| 662 | |||
| 663 | // update display name | ||
| 664 | hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); | ||
| 665 | ExitOnFailure(hr, "Failed to update name and publisher."); | ||
| 666 | |||
| 667 | // DisplayVersion: provided by UI | ||
| 668 | if (pRegistration->sczDisplayVersion) | ||
| 669 | { | ||
| 670 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion); | ||
| 671 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION); | ||
| 672 | } | ||
| 673 | |||
| 674 | // Publisher: provided by UI | ||
| 675 | hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher); | ||
| 676 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher); | ||
| 677 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER); | ||
| 678 | |||
| 679 | // HelpLink: provided by UI | ||
| 680 | if (pRegistration->sczHelpLink) | ||
| 681 | { | ||
| 682 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink); | ||
| 683 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK); | ||
| 684 | } | ||
| 685 | |||
| 686 | // HelpTelephone: provided by UI | ||
| 687 | if (pRegistration->sczHelpTelephone) | ||
| 688 | { | ||
| 689 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone); | ||
| 690 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE); | ||
| 691 | } | ||
| 692 | |||
| 693 | // URLInfoAbout, provided by UI | ||
| 694 | if (pRegistration->sczAboutUrl) | ||
| 695 | { | ||
| 696 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl); | ||
| 697 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT); | ||
| 698 | } | ||
| 699 | |||
| 700 | // URLUpdateInfo, provided by UI | ||
| 701 | if (pRegistration->sczUpdateUrl) | ||
| 702 | { | ||
| 703 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl); | ||
| 704 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO); | ||
| 705 | } | ||
| 706 | |||
| 707 | // ParentDisplayName | ||
| 708 | if (pRegistration->sczParentDisplayName) | ||
| 709 | { | ||
| 710 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName); | ||
| 711 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME); | ||
| 712 | |||
| 713 | // Need to write the ParentKeyName but can be set to anything. | ||
| 714 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName); | ||
| 715 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME); | ||
| 716 | } | ||
| 717 | |||
| 718 | // Comments, provided by UI | ||
| 719 | if (pRegistration->sczComments) | ||
| 720 | { | ||
| 721 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments); | ||
| 722 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS); | ||
| 723 | } | ||
| 724 | |||
| 725 | // Contact, provided by UI | ||
| 726 | if (pRegistration->sczContact) | ||
| 727 | { | ||
| 728 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact); | ||
| 729 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT); | ||
| 730 | } | ||
| 731 | |||
| 732 | // InstallLocation: provided by UI | ||
| 733 | // TODO: need to figure out what "InstallLocation" means in a chainer. <smile/> | ||
| 734 | |||
| 735 | // NoModify | ||
| 736 | if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify) | ||
| 737 | { | ||
| 738 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1); | ||
| 739 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY); | ||
| 740 | } | ||
| 741 | else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything) | ||
| 742 | { | ||
| 743 | // ModifyPath: [path to exe] /modify | ||
| 744 | hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath); | ||
| 745 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH); | ||
| 746 | |||
| 747 | // NoElevateOnModify: 1 | ||
| 748 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1); | ||
| 749 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY); | ||
| 750 | } | ||
| 751 | |||
| 752 | // NoRemove: should this be allowed? | ||
| 753 | if (pRegistration->fNoRemoveDefined) | ||
| 754 | { | ||
| 755 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove); | ||
| 756 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE); | ||
| 757 | } | ||
| 758 | |||
| 759 | // Conditionally hide the ARP entry. | ||
| 760 | if (!pRegistration->fRegisterArp) | ||
| 761 | { | ||
| 762 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1); | ||
| 763 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); | ||
| 764 | } | ||
| 765 | |||
| 766 | // QuietUninstallString: [path to exe] /uninstall /quiet | ||
| 767 | hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath); | ||
| 768 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING); | ||
| 769 | |||
| 770 | // UninstallString, [path to exe] | ||
| 771 | // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise, | ||
| 772 | // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away. | ||
| 773 | LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall"; | ||
| 774 | hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters); | ||
| 775 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING); | ||
| 776 | |||
| 777 | if (pRegistration->softwareTags.cSoftwareTags) | ||
| 778 | { | ||
| 779 | hr = WriteSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); | ||
| 780 | ExitOnFailure(hr, "Failed to write software tags."); | ||
| 781 | } | ||
| 782 | |||
| 783 | // Update registration. | ||
| 784 | if (pRegistration->update.fRegisterUpdate) | ||
| 785 | { | ||
| 786 | hr = WriteUpdateRegistration(pRegistration, pVariables); | ||
| 787 | ExitOnFailure(hr, "Failed to write update registration."); | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | // Update estimated size. | ||
| 792 | if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE) | ||
| 793 | { | ||
| 794 | qwEstimatedSize /= 1024; // Convert bytes to KB | ||
| 795 | if (0 < qwEstimatedSize) | ||
| 796 | { | ||
| 797 | if (DWORD_MAX < qwEstimatedSize) | ||
| 798 | { | ||
| 799 | // ARP doesn't support QWORDs here | ||
| 800 | dwSize = DWORD_MAX; | ||
| 801 | } | ||
| 802 | else | ||
| 803 | { | ||
| 804 | dwSize = static_cast<DWORD>(qwEstimatedSize); | ||
| 805 | } | ||
| 806 | |||
| 807 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize); | ||
| 808 | ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | // Register the bundle dependency key. | ||
| 813 | if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction) | ||
| 814 | { | ||
| 815 | hr = DependencyRegisterBundle(pRegistration); | ||
| 816 | ExitOnFailure(hr, "Failed to register the bundle dependency key."); | ||
| 817 | } | ||
| 818 | |||
| 819 | // update resume mode | ||
| 820 | hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); | ||
| 821 | ExitOnFailure(hr, "Failed to update resume mode."); | ||
| 822 | |||
| 823 | LExit: | ||
| 824 | ReleaseStr(sczPublisher); | ||
| 825 | ReleaseRegKey(hkRegistration); | ||
| 826 | |||
| 827 | return hr; | ||
| 828 | } | ||
| 829 | |||
| 830 | |||
| 831 | /******************************************************************* | ||
| 832 | RegistrationSessionResume - Resumes a previous run session. | ||
| 833 | |||
| 834 | *******************************************************************/ | ||
| 835 | extern "C" HRESULT RegistrationSessionResume( | ||
| 836 | __in BURN_REGISTRATION* pRegistration, | ||
| 837 | __in BURN_VARIABLES* pVariables | ||
| 838 | ) | ||
| 839 | { | ||
| 840 | HRESULT hr = S_OK; | ||
| 841 | HKEY hkRegistration = NULL; | ||
| 842 | |||
| 843 | // open registration key | ||
| 844 | hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); | ||
| 845 | ExitOnFailure(hr, "Failed to open registration key."); | ||
| 846 | |||
| 847 | // update resume mode | ||
| 848 | hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE); | ||
| 849 | ExitOnFailure(hr, "Failed to update resume mode."); | ||
| 850 | |||
| 851 | // update display name | ||
| 852 | hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration); | ||
| 853 | ExitOnFailure(hr, "Failed to update name and publisher."); | ||
| 854 | |||
| 855 | LExit: | ||
| 856 | ReleaseRegKey(hkRegistration); | ||
| 857 | |||
| 858 | return hr; | ||
| 859 | } | ||
| 860 | |||
| 861 | |||
| 862 | /******************************************************************* | ||
| 863 | RegistrationSessionEnd - Unregisters a run session from the system. | ||
| 864 | |||
| 865 | *******************************************************************/ | ||
| 866 | extern "C" HRESULT RegistrationSessionEnd( | ||
| 867 | __in BURN_REGISTRATION* pRegistration, | ||
| 868 | __in BURN_RESUME_MODE resumeMode, | ||
| 869 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 870 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction | ||
| 871 | ) | ||
| 872 | { | ||
| 873 | HRESULT hr = S_OK; | ||
| 874 | LPWSTR sczRebootRequiredKey = NULL; | ||
| 875 | HKEY hkRebootRequired = NULL; | ||
| 876 | HKEY hkRegistration = NULL; | ||
| 877 | |||
| 878 | LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume)); | ||
| 879 | |||
| 880 | // If a restart is required for any reason, write a volatile registry key to track of | ||
| 881 | // of that fact until the reboot has taken place. | ||
| 882 | if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) | ||
| 883 | { | ||
| 884 | // We'll write the volatile registry key right next to the bundle ARP registry key | ||
| 885 | // because that's easy. This is all best effort since the worst case just means in | ||
| 886 | // the rare case the user launches the same install again before taking the restart | ||
| 887 | // the BA won't know a restart was still required. | ||
| 888 | hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); | ||
| 889 | if (SUCCEEDED(hr)) | ||
| 890 | { | ||
| 891 | hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL); | ||
| 892 | } | ||
| 893 | |||
| 894 | if (FAILED(hr)) | ||
| 895 | { | ||
| 896 | ExitTrace(hr, "Failed to write volatile reboot required registry key."); | ||
| 897 | hr = S_OK; | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | // If no resume mode, then remove the bundle registration. | ||
| 902 | if (BURN_RESUME_MODE_NONE == resumeMode) | ||
| 903 | { | ||
| 904 | // If we just registered the bundle dependency but something went wrong and caused us to not | ||
| 905 | // keep the bundle registration (like rollback) or we are supposed to unregister the bundle | ||
| 906 | // dependency when unregistering the bundle, do so. | ||
| 907 | if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction || | ||
| 908 | BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction) | ||
| 909 | { | ||
| 910 | // Remove the bundle dependency key. | ||
| 911 | DependencyUnregisterBundle(pRegistration); | ||
| 912 | } | ||
| 913 | |||
| 914 | // Delete update registration key. | ||
| 915 | if (pRegistration->update.fRegisterUpdate) | ||
| 916 | { | ||
| 917 | RemoveUpdateRegistration(pRegistration); | ||
| 918 | } | ||
| 919 | |||
| 920 | RemoveSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags); | ||
| 921 | |||
| 922 | // Delete registration key. | ||
| 923 | hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE); | ||
| 924 | if (E_FILENOTFOUND != hr) | ||
| 925 | { | ||
| 926 | ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey); | ||
| 927 | } | ||
| 928 | |||
| 929 | CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId); | ||
| 930 | } | ||
| 931 | else // the mode needs to be updated so open the registration key. | ||
| 932 | { | ||
| 933 | // Open registration key. | ||
| 934 | hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); | ||
| 935 | ExitOnFailure(hr, "Failed to open registration key."); | ||
| 936 | } | ||
| 937 | |||
| 938 | // Update resume mode. | ||
| 939 | hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart); | ||
| 940 | ExitOnFailure(hr, "Failed to update resume mode."); | ||
| 941 | |||
| 942 | LExit: | ||
| 943 | ReleaseRegKey(hkRegistration); | ||
| 944 | ReleaseRegKey(hkRebootRequired); | ||
| 945 | ReleaseStr(sczRebootRequiredKey); | ||
| 946 | |||
| 947 | return hr; | ||
| 948 | } | ||
| 949 | |||
| 950 | /******************************************************************* | ||
| 951 | RegistrationSaveState - Saves an engine state BLOB for retreval after a resume. | ||
| 952 | |||
| 953 | *******************************************************************/ | ||
| 954 | extern "C" HRESULT RegistrationSaveState( | ||
| 955 | __in BURN_REGISTRATION* pRegistration, | ||
| 956 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 957 | __in SIZE_T cbBuffer | ||
| 958 | ) | ||
| 959 | { | ||
| 960 | HRESULT hr = S_OK; | ||
| 961 | |||
| 962 | // write data to file | ||
| 963 | hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); | ||
| 964 | if (E_PATHNOTFOUND == hr) | ||
| 965 | { | ||
| 966 | // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either? | ||
| 967 | hr = S_OK; | ||
| 968 | } | ||
| 969 | ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); | ||
| 970 | |||
| 971 | LExit: | ||
| 972 | return hr; | ||
| 973 | } | ||
| 974 | |||
| 975 | /******************************************************************* | ||
| 976 | RegistrationLoadState - Loads a previously stored engine state BLOB. | ||
| 977 | |||
| 978 | *******************************************************************/ | ||
| 979 | extern "C" HRESULT RegistrationLoadState( | ||
| 980 | __in BURN_REGISTRATION* pRegistration, | ||
| 981 | __out_bcount(*pcbBuffer) BYTE** ppbBuffer, | ||
| 982 | __out DWORD* pcbBuffer | ||
| 983 | ) | ||
| 984 | { | ||
| 985 | // read data from file | ||
| 986 | HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile); | ||
| 987 | return hr; | ||
| 988 | } | ||
| 989 | |||
| 990 | /******************************************************************* | ||
| 991 | RegistrationGetResumeCommandLine - Gets the resume command line from the registry | ||
| 992 | |||
| 993 | *******************************************************************/ | ||
| 994 | extern "C" HRESULT RegistrationGetResumeCommandLine( | ||
| 995 | __in const BURN_REGISTRATION* pRegistration, | ||
| 996 | __deref_out_z LPWSTR* psczResumeCommandLine | ||
| 997 | ) | ||
| 998 | { | ||
| 999 | HRESULT hr = S_OK; | ||
| 1000 | HKEY hkRegistration = NULL; | ||
| 1001 | |||
| 1002 | // open registration key | ||
| 1003 | hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); | ||
| 1004 | if (SUCCEEDED(hr)) | ||
| 1005 | { | ||
| 1006 | hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | // Not finding the key or value is okay. | ||
| 1010 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 1011 | { | ||
| 1012 | hr = S_OK; | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | ReleaseRegKey(hkRegistration); | ||
| 1016 | |||
| 1017 | return hr; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | |||
| 1021 | // internal helper functions | ||
| 1022 | |||
| 1023 | static HRESULT ParseSoftwareTagsFromXml( | ||
| 1024 | __in IXMLDOMNode* pixnRegistrationNode, | ||
| 1025 | __out BURN_SOFTWARE_TAG** prgSoftwareTags, | ||
| 1026 | __out DWORD* pcSoftwareTags | ||
| 1027 | ) | ||
| 1028 | { | ||
| 1029 | HRESULT hr = S_OK; | ||
| 1030 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 1031 | IXMLDOMNode* pixnNode = NULL; | ||
| 1032 | DWORD cNodes = 0; | ||
| 1033 | |||
| 1034 | BURN_SOFTWARE_TAG* pSoftwareTags = NULL; | ||
| 1035 | BSTR bstrTagXml = NULL; | ||
| 1036 | |||
| 1037 | // select tag nodes | ||
| 1038 | hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes); | ||
| 1039 | ExitOnFailure(hr, "Failed to select software tag nodes."); | ||
| 1040 | |||
| 1041 | // get tag node count | ||
| 1042 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 1043 | ExitOnFailure(hr, "Failed to get software tag count."); | ||
| 1044 | |||
| 1045 | if (cNodes) | ||
| 1046 | { | ||
| 1047 | pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE); | ||
| 1048 | ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs."); | ||
| 1049 | |||
| 1050 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 1051 | { | ||
| 1052 | BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i]; | ||
| 1053 | |||
| 1054 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 1055 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 1056 | |||
| 1057 | hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename); | ||
| 1058 | ExitOnFailure(hr, "Failed to get @Filename."); | ||
| 1059 | |||
| 1060 | hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); | ||
| 1061 | ExitOnFailure(hr, "Failed to get @Regid."); | ||
| 1062 | |||
| 1063 | hr = XmlGetText(pixnNode, &bstrTagXml); | ||
| 1064 | ExitOnFailure(hr, "Failed to get SoftwareTag text."); | ||
| 1065 | |||
| 1066 | hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8); | ||
| 1067 | ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8"); | ||
| 1068 | |||
| 1069 | // prepare next iteration | ||
| 1070 | ReleaseNullBSTR(bstrTagXml); | ||
| 1071 | ReleaseNullObject(pixnNode); | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | *pcSoftwareTags = cNodes; | ||
| 1076 | *prgSoftwareTags = pSoftwareTags; | ||
| 1077 | pSoftwareTags = NULL; | ||
| 1078 | |||
| 1079 | hr = S_OK; | ||
| 1080 | |||
| 1081 | LExit: | ||
| 1082 | ReleaseBSTR(bstrTagXml); | ||
| 1083 | ReleaseObject(pixnNode); | ||
| 1084 | ReleaseObject(pixnNodes); | ||
| 1085 | ReleaseMem(pSoftwareTags); | ||
| 1086 | |||
| 1087 | return hr; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | static HRESULT SetPaths( | ||
| 1091 | __in BURN_REGISTRATION* pRegistration | ||
| 1092 | ) | ||
| 1093 | { | ||
| 1094 | HRESULT hr = S_OK; | ||
| 1095 | LPWSTR sczCacheDirectory = NULL; | ||
| 1096 | |||
| 1097 | // save registration key root | ||
| 1098 | pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 1099 | |||
| 1100 | // build uninstall registry key path | ||
| 1101 | hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); | ||
| 1102 | ExitOnFailure(hr, "Failed to build uninstall registry key path."); | ||
| 1103 | |||
| 1104 | // build cache directory | ||
| 1105 | hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory); | ||
| 1106 | ExitOnFailure(hr, "Failed to build cache directory."); | ||
| 1107 | |||
| 1108 | // build cached executable path | ||
| 1109 | hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath); | ||
| 1110 | ExitOnFailure(hr, "Failed to build cached executable path."); | ||
| 1111 | |||
| 1112 | // build state file path | ||
| 1113 | hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory); | ||
| 1114 | ExitOnFailure(hr, "Failed to build state file path."); | ||
| 1115 | |||
| 1116 | LExit: | ||
| 1117 | ReleaseStr(sczCacheDirectory); | ||
| 1118 | return hr; | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | static HRESULT GetBundleManufacturer( | ||
| 1122 | __in BURN_REGISTRATION* pRegistration, | ||
| 1123 | __in BURN_VARIABLES* pVariables, | ||
| 1124 | __out LPWSTR* psczBundleManufacturer | ||
| 1125 | ) | ||
| 1126 | { | ||
| 1127 | HRESULT hr = S_OK; | ||
| 1128 | |||
| 1129 | hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer); | ||
| 1130 | if (E_NOTFOUND == hr) | ||
| 1131 | { | ||
| 1132 | hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE); | ||
| 1133 | ExitOnFailure(hr, "Failed to set bundle manufacturer."); | ||
| 1134 | |||
| 1135 | hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0); | ||
| 1136 | } | ||
| 1137 | ExitOnFailure(hr, "Failed to get bundle manufacturer."); | ||
| 1138 | |||
| 1139 | LExit: | ||
| 1140 | return hr; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | static HRESULT GetBundleName( | ||
| 1144 | __in BURN_REGISTRATION* pRegistration, | ||
| 1145 | __in BURN_VARIABLES* pVariables, | ||
| 1146 | __out LPWSTR* psczBundleName | ||
| 1147 | ) | ||
| 1148 | { | ||
| 1149 | HRESULT hr = S_OK; | ||
| 1150 | |||
| 1151 | hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName); | ||
| 1152 | if (E_NOTFOUND == hr) | ||
| 1153 | { | ||
| 1154 | hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE); | ||
| 1155 | ExitOnFailure(hr, "Failed to set bundle name."); | ||
| 1156 | |||
| 1157 | hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0); | ||
| 1158 | } | ||
| 1159 | ExitOnFailure(hr, "Failed to get bundle name."); | ||
| 1160 | |||
| 1161 | LExit: | ||
| 1162 | return hr; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | static HRESULT UpdateResumeMode( | ||
| 1166 | __in BURN_REGISTRATION* pRegistration, | ||
| 1167 | __in HKEY hkRegistration, | ||
| 1168 | __in BURN_RESUME_MODE resumeMode, | ||
| 1169 | __in BOOL fRestartInitiated | ||
| 1170 | ) | ||
| 1171 | { | ||
| 1172 | HRESULT hr = S_OK; | ||
| 1173 | DWORD er = ERROR_SUCCESS; | ||
| 1174 | HKEY hkRebootRequired = NULL; | ||
| 1175 | HKEY hkRun = NULL; | ||
| 1176 | LPWSTR sczResumeCommandLine = NULL; | ||
| 1177 | LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; | ||
| 1178 | OS_VERSION osv = OS_VERSION_UNKNOWN; | ||
| 1179 | DWORD dwServicePack = 0; | ||
| 1180 | |||
| 1181 | LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); | ||
| 1182 | |||
| 1183 | // On Windows XP and Server 2003, write the resume information to the Run key | ||
| 1184 | // instead of RunOnce. That avoids the problem that driver installation might | ||
| 1185 | // trigger RunOnce commands to be executed before the reboot. | ||
| 1186 | OsGetVersion(&osv, &dwServicePack); | ||
| 1187 | if (osv < OS_VERSION_VISTA) | ||
| 1188 | { | ||
| 1189 | sczResumeKey = REGISTRY_RUN_KEY; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | // write resume information | ||
| 1193 | if (hkRegistration) | ||
| 1194 | { | ||
| 1195 | // write Resume value | ||
| 1196 | hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode); | ||
| 1197 | ExitOnFailure(hr, "Failed to write Resume value."); | ||
| 1198 | |||
| 1199 | // Write the Installed value *only* when the mode is ARP. This will tell us | ||
| 1200 | // that the bundle considers itself "installed" on the machine. Note that we | ||
| 1201 | // never change the value to "0" after that. The bundle will be considered | ||
| 1202 | // "uninstalled" when all of the registration is removed. | ||
| 1203 | if (BURN_RESUME_MODE_ARP == resumeMode) | ||
| 1204 | { | ||
| 1205 | // Write Installed value. | ||
| 1206 | hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1); | ||
| 1207 | ExitOnFailure(hr, "Failed to write Installed value."); | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | // If the engine is active write the run key so we resume if there is an unexpected | ||
| 1212 | // power loss. Also, if a restart was initiated in the middle of the chain then | ||
| 1213 | // ensure the run key exists (it should since going active would have written it). | ||
| 1214 | // Do not write the run key when embedded since the containing bundle | ||
| 1215 | // is expected to detect for and restart the embedded bundle. | ||
| 1216 | if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume) | ||
| 1217 | { | ||
| 1218 | // append RunOnce switch | ||
| 1219 | hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE); | ||
| 1220 | ExitOnFailure(hr, "Failed to format resume command line for RunOnce."); | ||
| 1221 | |||
| 1222 | // write run key | ||
| 1223 | hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); | ||
| 1224 | ExitOnFailure(hr, "Failed to create run key."); | ||
| 1225 | |||
| 1226 | hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine); | ||
| 1227 | ExitOnFailure(hr, "Failed to write run key value."); | ||
| 1228 | |||
| 1229 | hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine); | ||
| 1230 | ExitOnFailure(hr, "Failed to write resume command line value."); | ||
| 1231 | } | ||
| 1232 | else // delete run key value | ||
| 1233 | { | ||
| 1234 | hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); | ||
| 1235 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 1236 | { | ||
| 1237 | hr = S_OK; | ||
| 1238 | } | ||
| 1239 | else | ||
| 1240 | { | ||
| 1241 | ExitOnWin32Error(er, hr, "Failed to open run key."); | ||
| 1242 | |||
| 1243 | er = ::RegDeleteValueW(hkRun, pRegistration->sczId); | ||
| 1244 | if (ERROR_FILE_NOT_FOUND == er) | ||
| 1245 | { | ||
| 1246 | er = ERROR_SUCCESS; | ||
| 1247 | } | ||
| 1248 | ExitOnWin32Error(er, hr, "Failed to delete run key value."); | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | if (hkRegistration) | ||
| 1252 | { | ||
| 1253 | er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE); | ||
| 1254 | if (ERROR_FILE_NOT_FOUND == er) | ||
| 1255 | { | ||
| 1256 | er = ERROR_SUCCESS; | ||
| 1257 | } | ||
| 1258 | ExitOnWin32Error(er, hr, "Failed to delete resume command line value."); | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | LExit: | ||
| 1263 | ReleaseStr(sczResumeCommandLine); | ||
| 1264 | ReleaseRegKey(hkRebootRequired); | ||
| 1265 | ReleaseRegKey(hkRun); | ||
| 1266 | |||
| 1267 | return hr; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | static HRESULT ParseRelatedCodes( | ||
| 1271 | __in BURN_REGISTRATION* pRegistration, | ||
| 1272 | __in IXMLDOMNode* pixnBundle | ||
| 1273 | ) | ||
| 1274 | { | ||
| 1275 | HRESULT hr = S_OK; | ||
| 1276 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 1277 | IXMLDOMNode* pixnElement = NULL; | ||
| 1278 | LPWSTR sczAction = NULL; | ||
| 1279 | LPWSTR sczId = NULL; | ||
| 1280 | DWORD cElements = 0; | ||
| 1281 | |||
| 1282 | hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes); | ||
| 1283 | ExitOnFailure(hr, "Failed to get RelatedBundle nodes"); | ||
| 1284 | |||
| 1285 | hr = pixnNodes->get_length((long*)&cElements); | ||
| 1286 | ExitOnFailure(hr, "Failed to get RelatedBundle element count."); | ||
| 1287 | |||
| 1288 | for (DWORD i = 0; i < cElements; ++i) | ||
| 1289 | { | ||
| 1290 | hr = XmlNextElement(pixnNodes, &pixnElement, NULL); | ||
| 1291 | ExitOnFailure(hr, "Failed to get next RelatedBundle element."); | ||
| 1292 | |||
| 1293 | hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction); | ||
| 1294 | ExitOnFailure(hr, "Failed to get @Action."); | ||
| 1295 | |||
| 1296 | hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId); | ||
| 1297 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 1298 | |||
| 1299 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1)) | ||
| 1300 | { | ||
| 1301 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5); | ||
| 1302 | ExitOnFailure(hr, "Failed to resize Detect code array in registration"); | ||
| 1303 | |||
| 1304 | pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId; | ||
| 1305 | sczId = NULL; | ||
| 1306 | ++pRegistration->cDetectCodes; | ||
| 1307 | } | ||
| 1308 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1)) | ||
| 1309 | { | ||
| 1310 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5); | ||
| 1311 | ExitOnFailure(hr, "Failed to resize Upgrade code array in registration"); | ||
| 1312 | |||
| 1313 | pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId; | ||
| 1314 | sczId = NULL; | ||
| 1315 | ++pRegistration->cUpgradeCodes; | ||
| 1316 | } | ||
| 1317 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1)) | ||
| 1318 | { | ||
| 1319 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5); | ||
| 1320 | ExitOnFailure(hr, "Failed to resize Addon code array in registration"); | ||
| 1321 | |||
| 1322 | pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId; | ||
| 1323 | sczId = NULL; | ||
| 1324 | ++pRegistration->cAddonCodes; | ||
| 1325 | } | ||
| 1326 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1)) | ||
| 1327 | { | ||
| 1328 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5); | ||
| 1329 | ExitOnFailure(hr, "Failed to resize Patch code array in registration"); | ||
| 1330 | |||
| 1331 | pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId; | ||
| 1332 | sczId = NULL; | ||
| 1333 | ++pRegistration->cPatchCodes; | ||
| 1334 | } | ||
| 1335 | else | ||
| 1336 | { | ||
| 1337 | hr = E_INVALIDARG; | ||
| 1338 | ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction); | ||
| 1339 | } | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | LExit: | ||
| 1343 | ReleaseObject(pixnNodes); | ||
| 1344 | ReleaseObject(pixnElement); | ||
| 1345 | ReleaseStr(sczAction); | ||
| 1346 | ReleaseStr(sczId); | ||
| 1347 | |||
| 1348 | return hr; | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | static HRESULT FormatUpdateRegistrationKey( | ||
| 1352 | __in BURN_REGISTRATION* pRegistration, | ||
| 1353 | __out_z LPWSTR* psczKey | ||
| 1354 | ) | ||
| 1355 | { | ||
| 1356 | HRESULT hr = S_OK; | ||
| 1357 | LPWSTR sczKey = NULL; | ||
| 1358 | |||
| 1359 | hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer); | ||
| 1360 | ExitOnFailure(hr, "Failed to format the key path for update registration."); | ||
| 1361 | |||
| 1362 | if (pRegistration->update.sczProductFamily) | ||
| 1363 | { | ||
| 1364 | hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily); | ||
| 1365 | ExitOnFailure(hr, "Failed to format the key path for update registration."); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0); | ||
| 1369 | ExitOnFailure(hr, "Failed to format the key path for update registration."); | ||
| 1370 | |||
| 1371 | *psczKey = sczKey; | ||
| 1372 | sczKey = NULL; | ||
| 1373 | |||
| 1374 | LExit: | ||
| 1375 | ReleaseStr(sczKey); | ||
| 1376 | |||
| 1377 | return hr; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | static HRESULT WriteSoftwareTags( | ||
| 1381 | __in BOOL fPerMachine, | ||
| 1382 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
| 1383 | ) | ||
| 1384 | { | ||
| 1385 | HRESULT hr = S_OK; | ||
| 1386 | LPWSTR sczRootFolder = NULL; | ||
| 1387 | LPWSTR sczRegidFolder = NULL; | ||
| 1388 | LPWSTR sczPath = NULL; | ||
| 1389 | |||
| 1390 | hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); | ||
| 1391 | ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 1392 | |||
| 1393 | for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) | ||
| 1394 | { | ||
| 1395 | BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; | ||
| 1396 | |||
| 1397 | hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); | ||
| 1398 | ExitOnFailure(hr, "Failed to allocate regid folder path."); | ||
| 1399 | |||
| 1400 | hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); | ||
| 1401 | ExitOnFailure(hr, "Failed to allocate regid folder path."); | ||
| 1402 | |||
| 1403 | hr = DirEnsureExists(sczRegidFolder, NULL); | ||
| 1404 | ExitOnFailure(hr, "Failed to create regid folder: %ls", sczRegidFolder); | ||
| 1405 | |||
| 1406 | hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast<LPBYTE>(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); | ||
| 1407 | ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | LExit: | ||
| 1411 | ReleaseStr(sczPath); | ||
| 1412 | ReleaseStr(sczRegidFolder); | ||
| 1413 | ReleaseStr(sczRootFolder); | ||
| 1414 | |||
| 1415 | return hr; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | static HRESULT RemoveSoftwareTags( | ||
| 1419 | __in BOOL fPerMachine, | ||
| 1420 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
| 1421 | ) | ||
| 1422 | { | ||
| 1423 | HRESULT hr = S_OK; | ||
| 1424 | LPWSTR sczRootFolder = NULL; | ||
| 1425 | LPWSTR sczRegidFolder = NULL; | ||
| 1426 | LPWSTR sczPath = NULL; | ||
| 1427 | |||
| 1428 | hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder); | ||
| 1429 | ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user"); | ||
| 1430 | |||
| 1431 | for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) | ||
| 1432 | { | ||
| 1433 | BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; | ||
| 1434 | |||
| 1435 | hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder); | ||
| 1436 | ExitOnFailure(hr, "Failed to allocate regid folder path."); | ||
| 1437 | |||
| 1438 | hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath); | ||
| 1439 | ExitOnFailure(hr, "Failed to allocate regid folder path."); | ||
| 1440 | |||
| 1441 | // Best effort to delete the software tag file and the regid folder. | ||
| 1442 | FileEnsureDelete(sczPath); | ||
| 1443 | |||
| 1444 | ::RemoveDirectoryW(sczRegidFolder); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | LExit: | ||
| 1448 | ReleaseStr(sczPath); | ||
| 1449 | ReleaseStr(sczRegidFolder); | ||
| 1450 | ReleaseStr(sczRootFolder); | ||
| 1451 | |||
| 1452 | return hr; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | static HRESULT WriteUpdateRegistration( | ||
| 1456 | __in BURN_REGISTRATION* pRegistration, | ||
| 1457 | __in BURN_VARIABLES* pVariables | ||
| 1458 | ) | ||
| 1459 | { | ||
| 1460 | HRESULT hr = S_OK; | ||
| 1461 | LPWSTR sczKey = NULL; | ||
| 1462 | HKEY hkKey = NULL; | ||
| 1463 | |||
| 1464 | hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); | ||
| 1465 | ExitOnFailure(hr, "Failed to get the formatted key path for update registration."); | ||
| 1466 | |||
| 1467 | hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey); | ||
| 1468 | ExitOnFailure(hr, "Failed to create the key for update registration."); | ||
| 1469 | |||
| 1470 | hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y"); | ||
| 1471 | ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled"); | ||
| 1472 | |||
| 1473 | hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName); | ||
| 1474 | ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName"); | ||
| 1475 | |||
| 1476 | hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion); | ||
| 1477 | ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion"); | ||
| 1478 | |||
| 1479 | hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher); | ||
| 1480 | ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher"); | ||
| 1481 | |||
| 1482 | if (pRegistration->update.sczDepartment) | ||
| 1483 | { | ||
| 1484 | hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment); | ||
| 1485 | ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup"); | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification); | ||
| 1489 | ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType"); | ||
| 1490 | |||
| 1491 | hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy"); | ||
| 1492 | ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy"); | ||
| 1493 | |||
| 1494 | hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate"); | ||
| 1495 | ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate"); | ||
| 1496 | |||
| 1497 | hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName"); | ||
| 1498 | ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName"); | ||
| 1499 | |||
| 1500 | hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion"); | ||
| 1501 | ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion"); | ||
| 1502 | |||
| 1503 | LExit: | ||
| 1504 | ReleaseRegKey(hkKey); | ||
| 1505 | ReleaseStr(sczKey); | ||
| 1506 | |||
| 1507 | return hr; | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | static HRESULT RemoveUpdateRegistration( | ||
| 1511 | __in BURN_REGISTRATION* pRegistration | ||
| 1512 | ) | ||
| 1513 | { | ||
| 1514 | HRESULT hr = S_OK; | ||
| 1515 | LPWSTR sczKey = NULL; | ||
| 1516 | LPWSTR sczPackageVersion = NULL; | ||
| 1517 | HKEY hkKey = NULL; | ||
| 1518 | BOOL fDeleteRegKey = TRUE; | ||
| 1519 | |||
| 1520 | hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); | ||
| 1521 | ExitOnFailure(hr, "Failed to format key for update registration."); | ||
| 1522 | |||
| 1523 | // Only delete if the uninstalling bundle's PackageVersion is the same as the | ||
| 1524 | // PackageVersion in the update registration key. | ||
| 1525 | // This is to support build to build upgrades | ||
| 1526 | hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey); | ||
| 1527 | if (SUCCEEDED(hr)) | ||
| 1528 | { | ||
| 1529 | hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion); | ||
| 1530 | if (SUCCEEDED(hr)) | ||
| 1531 | { | ||
| 1532 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1)) | ||
| 1533 | { | ||
| 1534 | fDeleteRegKey = FALSE; | ||
| 1535 | } | ||
| 1536 | } | ||
| 1537 | ReleaseRegKey(hkKey); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | // Unable to open the key or read the value is okay. | ||
| 1541 | hr = S_OK; | ||
| 1542 | |||
| 1543 | if (fDeleteRegKey) | ||
| 1544 | { | ||
| 1545 | hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE); | ||
| 1546 | if (E_FILENOTFOUND != hr) | ||
| 1547 | { | ||
| 1548 | ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey); | ||
| 1549 | } | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | LExit: | ||
| 1553 | ReleaseStr(sczPackageVersion); | ||
| 1554 | ReleaseStr(sczKey); | ||
| 1555 | |||
| 1556 | return hr; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | static HRESULT RegWriteStringVariable( | ||
| 1560 | __in HKEY hk, | ||
| 1561 | __in BURN_VARIABLES* pVariables, | ||
| 1562 | __in LPCWSTR wzVariable, | ||
| 1563 | __in LPCWSTR wzName | ||
| 1564 | ) | ||
| 1565 | { | ||
| 1566 | HRESULT hr = S_OK; | ||
| 1567 | LPWSTR sczValue = NULL; | ||
| 1568 | |||
| 1569 | hr = VariableGetString(pVariables, wzVariable, &sczValue); | ||
| 1570 | ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable); | ||
| 1571 | |||
| 1572 | hr = RegWriteString(hk, wzName, sczValue); | ||
| 1573 | ExitOnFailure(hr, "Failed to write %ls value.", wzName); | ||
| 1574 | |||
| 1575 | LExit: | ||
| 1576 | StrSecureZeroFreeString(sczValue); | ||
| 1577 | |||
| 1578 | return hr; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | static HRESULT UpdateBundleNameRegistration( | ||
| 1582 | __in BURN_REGISTRATION* pRegistration, | ||
| 1583 | __in BURN_VARIABLES* pVariables, | ||
| 1584 | __in HKEY hkRegistration | ||
| 1585 | ) | ||
| 1586 | { | ||
| 1587 | HRESULT hr = S_OK; | ||
| 1588 | LPWSTR sczDisplayName = NULL; | ||
| 1589 | |||
| 1590 | // DisplayName: provided by UI | ||
| 1591 | hr = GetBundleName(pRegistration, pVariables, &sczDisplayName); | ||
| 1592 | hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName); | ||
| 1593 | ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME); | ||
| 1594 | |||
| 1595 | LExit: | ||
| 1596 | ReleaseStr(sczDisplayName); | ||
| 1597 | |||
| 1598 | return hr; | ||
| 1599 | } | ||
diff --git a/src/engine/registration.h b/src/engine/registration.h new file mode 100644 index 00000000..c830a06d --- /dev/null +++ b/src/engine/registration.h | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | enum BURN_MODE; | ||
| 11 | enum BURN_DEPENDENCY_REGISTRATION_ACTION; | ||
| 12 | struct _BURN_LOGGING; | ||
| 13 | typedef _BURN_LOGGING BURN_LOGGING; | ||
| 14 | |||
| 15 | // constants | ||
| 16 | |||
| 17 | const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; | ||
| 18 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; | ||
| 19 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; | ||
| 20 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; | ||
| 21 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; | ||
| 22 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; | ||
| 23 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; | ||
| 24 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; | ||
| 25 | const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; | ||
| 26 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; | ||
| 27 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; | ||
| 28 | |||
| 29 | enum BURN_RESUME_MODE | ||
| 30 | { | ||
| 31 | BURN_RESUME_MODE_NONE, | ||
| 32 | BURN_RESUME_MODE_ACTIVE, | ||
| 33 | BURN_RESUME_MODE_SUSPEND, | ||
| 34 | BURN_RESUME_MODE_ARP, | ||
| 35 | BURN_RESUME_MODE_REBOOT_PENDING, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum BURN_REGISTRATION_MODIFY_TYPE | ||
| 39 | { | ||
| 40 | BURN_REGISTRATION_MODIFY_ENABLED, | ||
| 41 | BURN_REGISTRATION_MODIFY_DISABLE, | ||
| 42 | BURN_REGISTRATION_MODIFY_DISABLE_BUTTON, | ||
| 43 | }; | ||
| 44 | |||
| 45 | |||
| 46 | // structs | ||
| 47 | |||
| 48 | typedef struct _BURN_UPDATE_REGISTRATION | ||
| 49 | { | ||
| 50 | BOOL fRegisterUpdate; | ||
| 51 | LPWSTR sczManufacturer; | ||
| 52 | LPWSTR sczDepartment; | ||
| 53 | LPWSTR sczProductFamily; | ||
| 54 | LPWSTR sczName; | ||
| 55 | LPWSTR sczClassification; | ||
| 56 | } BURN_UPDATE_REGISTRATION; | ||
| 57 | |||
| 58 | typedef struct _BURN_RELATED_BUNDLE | ||
| 59 | { | ||
| 60 | BOOTSTRAPPER_RELATION_TYPE relationType; | ||
| 61 | |||
| 62 | DWORD64 qwVersion; | ||
| 63 | LPWSTR sczTag; | ||
| 64 | |||
| 65 | BURN_PACKAGE package; | ||
| 66 | } BURN_RELATED_BUNDLE; | ||
| 67 | |||
| 68 | typedef struct _BURN_RELATED_BUNDLES | ||
| 69 | { | ||
| 70 | BURN_RELATED_BUNDLE* rgRelatedBundles; | ||
| 71 | DWORD cRelatedBundles; | ||
| 72 | } BURN_RELATED_BUNDLES; | ||
| 73 | |||
| 74 | typedef struct _BURN_SOFTWARE_TAG | ||
| 75 | { | ||
| 76 | LPWSTR sczFilename; | ||
| 77 | LPWSTR sczRegid; | ||
| 78 | LPSTR sczTag; | ||
| 79 | } BURN_SOFTWARE_TAG; | ||
| 80 | |||
| 81 | typedef struct _BURN_SOFTWARE_TAGS | ||
| 82 | { | ||
| 83 | BURN_SOFTWARE_TAG* rgSoftwareTags; | ||
| 84 | DWORD cSoftwareTags; | ||
| 85 | } BURN_SOFTWARE_TAGS; | ||
| 86 | |||
| 87 | typedef struct _BURN_REGISTRATION | ||
| 88 | { | ||
| 89 | BOOL fPerMachine; | ||
| 90 | BOOL fRegisterArp; | ||
| 91 | BOOL fDisableResume; | ||
| 92 | BOOL fInstalled; | ||
| 93 | LPWSTR sczId; | ||
| 94 | LPWSTR sczTag; | ||
| 95 | |||
| 96 | LPWSTR *rgsczDetectCodes; | ||
| 97 | DWORD cDetectCodes; | ||
| 98 | |||
| 99 | LPWSTR *rgsczUpgradeCodes; | ||
| 100 | DWORD cUpgradeCodes; | ||
| 101 | |||
| 102 | LPWSTR *rgsczAddonCodes; | ||
| 103 | DWORD cAddonCodes; | ||
| 104 | |||
| 105 | LPWSTR *rgsczPatchCodes; | ||
| 106 | DWORD cPatchCodes; | ||
| 107 | |||
| 108 | DWORD64 qwVersion; | ||
| 109 | LPWSTR sczActiveParent; | ||
| 110 | LPWSTR sczProviderKey; | ||
| 111 | LPWSTR sczExecutableName; | ||
| 112 | |||
| 113 | // paths | ||
| 114 | HKEY hkRoot; | ||
| 115 | LPWSTR sczRegistrationKey; | ||
| 116 | LPWSTR sczCacheExecutablePath; | ||
| 117 | LPWSTR sczResumeCommandLine; | ||
| 118 | LPWSTR sczStateFile; | ||
| 119 | |||
| 120 | // ARP registration | ||
| 121 | LPWSTR sczDisplayName; | ||
| 122 | LPWSTR sczDisplayVersion; | ||
| 123 | LPWSTR sczPublisher; | ||
| 124 | LPWSTR sczHelpLink; | ||
| 125 | LPWSTR sczHelpTelephone; | ||
| 126 | LPWSTR sczAboutUrl; | ||
| 127 | LPWSTR sczUpdateUrl; | ||
| 128 | LPWSTR sczParentDisplayName; | ||
| 129 | LPWSTR sczComments; | ||
| 130 | //LPWSTR sczReadme; // TODO: this would be a file path | ||
| 131 | LPWSTR sczContact; | ||
| 132 | //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation | ||
| 133 | BURN_REGISTRATION_MODIFY_TYPE modify; | ||
| 134 | BOOL fNoRemoveDefined; | ||
| 135 | BOOL fNoRemove; | ||
| 136 | |||
| 137 | BURN_SOFTWARE_TAGS softwareTags; | ||
| 138 | |||
| 139 | // Update registration | ||
| 140 | BURN_UPDATE_REGISTRATION update; | ||
| 141 | |||
| 142 | // Only valid after detect. | ||
| 143 | BURN_RELATED_BUNDLES relatedBundles; | ||
| 144 | |||
| 145 | LPWSTR sczDetectedProviderKeyBundleId; | ||
| 146 | LPWSTR sczAncestors; | ||
| 147 | |||
| 148 | BOOL fEnabledForwardCompatibleBundle; | ||
| 149 | BURN_PACKAGE forwardCompatibleBundle; | ||
| 150 | } BURN_REGISTRATION; | ||
| 151 | |||
| 152 | |||
| 153 | // functions | ||
| 154 | |||
| 155 | HRESULT RegistrationParseFromXml( | ||
| 156 | __in BURN_REGISTRATION* pRegistration, | ||
| 157 | __in IXMLDOMNode* pixnBundle | ||
| 158 | ); | ||
| 159 | void RegistrationUninitialize( | ||
| 160 | __in BURN_REGISTRATION* pRegistration | ||
| 161 | ); | ||
| 162 | HRESULT RegistrationSetVariables( | ||
| 163 | __in BURN_REGISTRATION* pRegistration, | ||
| 164 | __in BURN_VARIABLES* pVariables | ||
| 165 | ); | ||
| 166 | HRESULT RegistrationDetectInstalled( | ||
| 167 | __in BURN_REGISTRATION* pRegistration, | ||
| 168 | __out BOOL* pfInstalled | ||
| 169 | ); | ||
| 170 | HRESULT RegistrationDetectResumeType( | ||
| 171 | __in BURN_REGISTRATION* pRegistration, | ||
| 172 | __out BOOTSTRAPPER_RESUME_TYPE* pResumeType | ||
| 173 | ); | ||
| 174 | HRESULT RegistrationDetectRelatedBundles( | ||
| 175 | __in BURN_REGISTRATION* pRegistration | ||
| 176 | ); | ||
| 177 | HRESULT RegistrationSessionBegin( | ||
| 178 | __in_z LPCWSTR wzEngineWorkingPath, | ||
| 179 | __in BURN_REGISTRATION* pRegistration, | ||
| 180 | __in BURN_VARIABLES* pVariables, | ||
| 181 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 182 | __in DWORD dwRegistrationOptions, | ||
| 183 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction, | ||
| 184 | __in DWORD64 qwEstimatedSize | ||
| 185 | ); | ||
| 186 | HRESULT RegistrationSessionResume( | ||
| 187 | __in BURN_REGISTRATION* pRegistration, | ||
| 188 | __in BURN_VARIABLES* pVariables | ||
| 189 | ); | ||
| 190 | HRESULT RegistrationSessionEnd( | ||
| 191 | __in BURN_REGISTRATION* pRegistration, | ||
| 192 | __in BURN_RESUME_MODE resumeMode, | ||
| 193 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 194 | __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction | ||
| 195 | ); | ||
| 196 | HRESULT RegistrationSaveState( | ||
| 197 | __in BURN_REGISTRATION* pRegistration, | ||
| 198 | __in_bcount_opt(cbBuffer) BYTE* pbBuffer, | ||
| 199 | __in_opt DWORD cbBuffer | ||
| 200 | ); | ||
| 201 | HRESULT RegistrationLoadState( | ||
| 202 | __in BURN_REGISTRATION* pRegistration, | ||
| 203 | __out_bcount(*pcbBuffer) BYTE** ppbBuffer, | ||
| 204 | __out DWORD* pcbBuffer | ||
| 205 | ); | ||
| 206 | HRESULT RegistrationGetResumeCommandLine( | ||
| 207 | __in const BURN_REGISTRATION* pRegistration, | ||
| 208 | __deref_out_z LPWSTR* psczResumeCommandLine | ||
| 209 | ); | ||
| 210 | |||
| 211 | |||
| 212 | #if defined(__cplusplus) | ||
| 213 | } | ||
| 214 | #endif | ||
diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp new file mode 100644 index 00000000..87794177 --- /dev/null +++ b/src/engine/relatedbundle.cpp | |||
| @@ -0,0 +1,457 @@ | |||
| 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 | |||
| 5 | // internal function declarations | ||
| 6 | |||
| 7 | static HRESULT LoadIfRelatedBundle( | ||
| 8 | __in BOOL fPerMachine, | ||
| 9 | __in HKEY hkUninstallKey, | ||
| 10 | __in_z LPCWSTR sczRelatedBundleId, | ||
| 11 | __in BURN_REGISTRATION* pRegistration, | ||
| 12 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 13 | ); | ||
| 14 | static HRESULT DetermineRelationType( | ||
| 15 | __in HKEY hkBundleId, | ||
| 16 | __in BURN_REGISTRATION* pRegistration, | ||
| 17 | __out BOOTSTRAPPER_RELATION_TYPE* pRelationType | ||
| 18 | ); | ||
| 19 | static HRESULT LoadRelatedBundleFromKey( | ||
| 20 | __in_z LPCWSTR wzRelatedBundleId, | ||
| 21 | __in HKEY hkBundleId, | ||
| 22 | __in BOOL fPerMachine, | ||
| 23 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 24 | __inout BURN_RELATED_BUNDLE *pRelatedBundle | ||
| 25 | ); | ||
| 26 | |||
| 27 | |||
| 28 | // function definitions | ||
| 29 | |||
| 30 | extern "C" HRESULT RelatedBundlesInitializeForScope( | ||
| 31 | __in BOOL fPerMachine, | ||
| 32 | __in BURN_REGISTRATION* pRegistration, | ||
| 33 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 34 | ) | ||
| 35 | { | ||
| 36 | HRESULT hr = S_OK; | ||
| 37 | HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
| 38 | HKEY hkUninstallKey = NULL; | ||
| 39 | LPWSTR sczRelatedBundleId = NULL; | ||
| 40 | |||
| 41 | hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey); | ||
| 42 | if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) | ||
| 43 | { | ||
| 44 | ExitFunction1(hr = S_OK); | ||
| 45 | } | ||
| 46 | ExitOnFailure(hr, "Failed to open uninstall registry key."); | ||
| 47 | |||
| 48 | for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) | ||
| 49 | { | ||
| 50 | hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId); | ||
| 51 | if (E_NOMOREITEMS == hr) | ||
| 52 | { | ||
| 53 | hr = S_OK; | ||
| 54 | break; | ||
| 55 | } | ||
| 56 | ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); | ||
| 57 | |||
| 58 | // If we did not find our bundle id, try to load the subkey as a related bundle. | ||
| 59 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1)) | ||
| 60 | { | ||
| 61 | // Ignore failures here since we'll often find products that aren't actually | ||
| 62 | // related bundles (or even bundles at all). | ||
| 63 | HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles); | ||
| 64 | UNREFERENCED_PARAMETER(hrRelatedBundle); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | LExit: | ||
| 69 | ReleaseStr(sczRelatedBundleId); | ||
| 70 | ReleaseRegKey(hkUninstallKey); | ||
| 71 | |||
| 72 | return hr; | ||
| 73 | } | ||
| 74 | |||
| 75 | extern "C" void RelatedBundlesUninitialize( | ||
| 76 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 77 | ) | ||
| 78 | { | ||
| 79 | if (pRelatedBundles->rgRelatedBundles) | ||
| 80 | { | ||
| 81 | for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i) | ||
| 82 | { | ||
| 83 | PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package); | ||
| 84 | ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag); | ||
| 85 | } | ||
| 86 | |||
| 87 | MemFree(pRelatedBundles->rgRelatedBundles); | ||
| 88 | } | ||
| 89 | |||
| 90 | memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES)); | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | // internal helper functions | ||
| 95 | |||
| 96 | static HRESULT LoadIfRelatedBundle( | ||
| 97 | __in BOOL fPerMachine, | ||
| 98 | __in HKEY hkUninstallKey, | ||
| 99 | __in_z LPCWSTR sczRelatedBundleId, | ||
| 100 | __in BURN_REGISTRATION* pRegistration, | ||
| 101 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 102 | ) | ||
| 103 | { | ||
| 104 | HRESULT hr = S_OK; | ||
| 105 | HKEY hkBundleId = NULL; | ||
| 106 | BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; | ||
| 107 | |||
| 108 | hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId); | ||
| 109 | ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId); | ||
| 110 | |||
| 111 | hr = DetermineRelationType(hkBundleId, pRegistration, &relationType); | ||
| 112 | if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType) | ||
| 113 | { | ||
| 114 | // Must not be a related bundle. | ||
| 115 | hr = E_NOTFOUND; | ||
| 116 | } | ||
| 117 | else // load the related bundle. | ||
| 118 | { | ||
| 119 | hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); | ||
| 120 | ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); | ||
| 121 | |||
| 122 | BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; | ||
| 123 | |||
| 124 | hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle); | ||
| 125 | ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId); | ||
| 126 | |||
| 127 | ++pRelatedBundles->cRelatedBundles; | ||
| 128 | } | ||
| 129 | |||
| 130 | LExit: | ||
| 131 | ReleaseRegKey(hkBundleId); | ||
| 132 | |||
| 133 | return hr; | ||
| 134 | } | ||
| 135 | |||
| 136 | static HRESULT DetermineRelationType( | ||
| 137 | __in HKEY hkBundleId, | ||
| 138 | __in BURN_REGISTRATION* pRegistration, | ||
| 139 | __out BOOTSTRAPPER_RELATION_TYPE* pRelationType | ||
| 140 | ) | ||
| 141 | { | ||
| 142 | HRESULT hr = S_OK; | ||
| 143 | LPWSTR* rgsczUpgradeCodes = NULL; | ||
| 144 | DWORD cUpgradeCodes = 0; | ||
| 145 | STRINGDICT_HANDLE sdUpgradeCodes = NULL; | ||
| 146 | LPWSTR* rgsczAddonCodes = NULL; | ||
| 147 | DWORD cAddonCodes = 0; | ||
| 148 | STRINGDICT_HANDLE sdAddonCodes = NULL; | ||
| 149 | LPWSTR* rgsczDetectCodes = NULL; | ||
| 150 | DWORD cDetectCodes = 0; | ||
| 151 | STRINGDICT_HANDLE sdDetectCodes = NULL; | ||
| 152 | LPWSTR* rgsczPatchCodes = NULL; | ||
| 153 | DWORD cPatchCodes = 0; | ||
| 154 | STRINGDICT_HANDLE sdPatchCodes = NULL; | ||
| 155 | |||
| 156 | *pRelationType = BOOTSTRAPPER_RELATION_NONE; | ||
| 157 | |||
| 158 | // All remaining operations should treat all related bundles as non-vital. | ||
| 159 | hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes); | ||
| 160 | if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr) | ||
| 161 | { | ||
| 162 | TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); | ||
| 163 | |||
| 164 | rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE)); | ||
| 165 | ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle."); | ||
| 166 | |||
| 167 | hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); | ||
| 168 | if (SUCCEEDED(hr)) | ||
| 169 | { | ||
| 170 | cUpgradeCodes = 1; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | // Compare upgrade codes. | ||
| 175 | if (SUCCEEDED(hr)) | ||
| 176 | { | ||
| 177 | hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); | ||
| 178 | ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); | ||
| 179 | |||
| 180 | // Upgrade relationship: when their upgrade codes match our upgrade codes. | ||
| 181 | hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); | ||
| 182 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 183 | { | ||
| 184 | hr = S_OK; | ||
| 185 | } | ||
| 186 | else | ||
| 187 | { | ||
| 188 | ExitOnFailure(hr, "Failed to do array search for upgrade code match."); | ||
| 189 | |||
| 190 | *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE; | ||
| 191 | ExitFunction(); | ||
| 192 | } | ||
| 193 | |||
| 194 | // Detect relationship: when their upgrade codes match our detect codes. | ||
| 195 | hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); | ||
| 196 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 197 | { | ||
| 198 | hr = S_OK; | ||
| 199 | } | ||
| 200 | else | ||
| 201 | { | ||
| 202 | ExitOnFailure(hr, "Failed to do array search for detect code match."); | ||
| 203 | |||
| 204 | *pRelationType = BOOTSTRAPPER_RELATION_DETECT; | ||
| 205 | ExitFunction(); | ||
| 206 | } | ||
| 207 | |||
| 208 | // Dependent relationship: when their upgrade codes match our addon codes. | ||
| 209 | hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); | ||
| 210 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 211 | { | ||
| 212 | hr = S_OK; | ||
| 213 | } | ||
| 214 | else | ||
| 215 | { | ||
| 216 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 217 | |||
| 218 | *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; | ||
| 219 | ExitFunction(); | ||
| 220 | } | ||
| 221 | |||
| 222 | // Dependent relationship: when their upgrade codes match our patch codes. | ||
| 223 | hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); | ||
| 224 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 225 | { | ||
| 226 | hr = S_OK; | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 231 | |||
| 232 | *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; | ||
| 233 | ExitFunction(); | ||
| 234 | } | ||
| 235 | |||
| 236 | ReleaseNullDict(sdUpgradeCodes); | ||
| 237 | ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes); | ||
| 238 | } | ||
| 239 | |||
| 240 | // Compare addon codes. | ||
| 241 | hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes); | ||
| 242 | if (SUCCEEDED(hr)) | ||
| 243 | { | ||
| 244 | hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); | ||
| 245 | ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); | ||
| 246 | |||
| 247 | // Addon relationship: when their addon codes match our detect codes. | ||
| 248 | hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); | ||
| 249 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 250 | { | ||
| 251 | hr = S_OK; | ||
| 252 | } | ||
| 253 | else | ||
| 254 | { | ||
| 255 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 256 | |||
| 257 | *pRelationType = BOOTSTRAPPER_RELATION_ADDON; | ||
| 258 | ExitFunction(); | ||
| 259 | } | ||
| 260 | |||
| 261 | // Addon relationship: when their addon codes match our upgrade codes. | ||
| 262 | hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); | ||
| 263 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 264 | { | ||
| 265 | hr = S_OK; | ||
| 266 | } | ||
| 267 | else | ||
| 268 | { | ||
| 269 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 270 | |||
| 271 | *pRelationType = BOOTSTRAPPER_RELATION_ADDON; | ||
| 272 | ExitFunction(); | ||
| 273 | } | ||
| 274 | |||
| 275 | ReleaseNullDict(sdAddonCodes); | ||
| 276 | ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes); | ||
| 277 | } | ||
| 278 | |||
| 279 | // Compare patch codes. | ||
| 280 | hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); | ||
| 281 | if (SUCCEEDED(hr)) | ||
| 282 | { | ||
| 283 | hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); | ||
| 284 | ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); | ||
| 285 | |||
| 286 | // Patch relationship: when their patch codes match our detect codes. | ||
| 287 | hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); | ||
| 288 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 289 | { | ||
| 290 | hr = S_OK; | ||
| 291 | } | ||
| 292 | else | ||
| 293 | { | ||
| 294 | ExitOnFailure(hr, "Failed to do array search for patch code match."); | ||
| 295 | |||
| 296 | *pRelationType = BOOTSTRAPPER_RELATION_PATCH; | ||
| 297 | ExitFunction(); | ||
| 298 | } | ||
| 299 | |||
| 300 | // Patch relationship: when their patch codes match our upgrade codes. | ||
| 301 | hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); | ||
| 302 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 303 | { | ||
| 304 | hr = S_OK; | ||
| 305 | } | ||
| 306 | else | ||
| 307 | { | ||
| 308 | ExitOnFailure(hr, "Failed to do array search for patch code match."); | ||
| 309 | |||
| 310 | *pRelationType = BOOTSTRAPPER_RELATION_PATCH; | ||
| 311 | ExitFunction(); | ||
| 312 | } | ||
| 313 | |||
| 314 | ReleaseNullDict(sdPatchCodes); | ||
| 315 | ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes); | ||
| 316 | } | ||
| 317 | |||
| 318 | // Compare detect codes. | ||
| 319 | hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); | ||
| 320 | if (SUCCEEDED(hr)) | ||
| 321 | { | ||
| 322 | hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); | ||
| 323 | ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); | ||
| 324 | |||
| 325 | // Detect relationship: when their detect codes match our detect codes. | ||
| 326 | hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); | ||
| 327 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 328 | { | ||
| 329 | hr = S_OK; | ||
| 330 | } | ||
| 331 | else | ||
| 332 | { | ||
| 333 | ExitOnFailure(hr, "Failed to do array search for detect code match."); | ||
| 334 | |||
| 335 | *pRelationType = BOOTSTRAPPER_RELATION_DETECT; | ||
| 336 | ExitFunction(); | ||
| 337 | } | ||
| 338 | |||
| 339 | // Dependent relationship: when their detect codes match our addon codes. | ||
| 340 | hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); | ||
| 341 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 342 | { | ||
| 343 | hr = S_OK; | ||
| 344 | } | ||
| 345 | else | ||
| 346 | { | ||
| 347 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 348 | |||
| 349 | *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; | ||
| 350 | ExitFunction(); | ||
| 351 | } | ||
| 352 | |||
| 353 | // Dependent relationship: when their detect codes match our patch codes. | ||
| 354 | hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); | ||
| 355 | if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) | ||
| 356 | { | ||
| 357 | hr = S_OK; | ||
| 358 | } | ||
| 359 | else | ||
| 360 | { | ||
| 361 | ExitOnFailure(hr, "Failed to do array search for addon code match."); | ||
| 362 | |||
| 363 | *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; | ||
| 364 | ExitFunction(); | ||
| 365 | } | ||
| 366 | |||
| 367 | ReleaseNullDict(sdDetectCodes); | ||
| 368 | ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes); | ||
| 369 | } | ||
| 370 | |||
| 371 | LExit: | ||
| 372 | if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType) | ||
| 373 | { | ||
| 374 | hr = E_NOTFOUND; | ||
| 375 | } | ||
| 376 | |||
| 377 | ReleaseDict(sdUpgradeCodes); | ||
| 378 | ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); | ||
| 379 | ReleaseDict(sdAddonCodes); | ||
| 380 | ReleaseStrArray(rgsczAddonCodes, cAddonCodes); | ||
| 381 | ReleaseDict(sdDetectCodes); | ||
| 382 | ReleaseStrArray(rgsczDetectCodes, cDetectCodes); | ||
| 383 | ReleaseDict(sdPatchCodes); | ||
| 384 | ReleaseStrArray(rgsczPatchCodes, cPatchCodes); | ||
| 385 | |||
| 386 | return hr; | ||
| 387 | } | ||
| 388 | |||
| 389 | static HRESULT LoadRelatedBundleFromKey( | ||
| 390 | __in_z LPCWSTR wzRelatedBundleId, | ||
| 391 | __in HKEY hkBundleId, | ||
| 392 | __in BOOL fPerMachine, | ||
| 393 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 394 | __inout BURN_RELATED_BUNDLE *pRelatedBundle | ||
| 395 | ) | ||
| 396 | { | ||
| 397 | HRESULT hr = S_OK; | ||
| 398 | DWORD64 qwEngineVersion = 0; | ||
| 399 | LPWSTR sczCachePath = NULL; | ||
| 400 | DWORD64 qwFileSize = 0; | ||
| 401 | BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; | ||
| 402 | |||
| 403 | hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion); | ||
| 404 | if (FAILED(hr)) | ||
| 405 | { | ||
| 406 | qwEngineVersion = 0; | ||
| 407 | hr = S_OK; | ||
| 408 | } | ||
| 409 | |||
| 410 | hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion); | ||
| 411 | ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId); | ||
| 412 | |||
| 413 | hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); | ||
| 414 | ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId); | ||
| 415 | |||
| 416 | hr = FileSize(sczCachePath, reinterpret_cast<LONGLONG *>(&qwFileSize)); | ||
| 417 | ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath); | ||
| 418 | |||
| 419 | hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey); | ||
| 420 | if (E_FILENOTFOUND != hr) | ||
| 421 | { | ||
| 422 | ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId); | ||
| 423 | |||
| 424 | dependencyProvider.fImported = TRUE; | ||
| 425 | |||
| 426 | hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion); | ||
| 427 | ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId); | ||
| 428 | |||
| 429 | hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName); | ||
| 430 | if (E_FILENOTFOUND != hr) | ||
| 431 | { | ||
| 432 | ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag); | ||
| 437 | if (E_FILENOTFOUND == hr) | ||
| 438 | { | ||
| 439 | hr = S_OK; | ||
| 440 | } | ||
| 441 | ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId); | ||
| 442 | |||
| 443 | pRelatedBundle->relationType = relationType; | ||
| 444 | |||
| 445 | hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType, | ||
| 446 | BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE, | ||
| 447 | L"-quiet", L"-repair -quiet", L"-uninstall -quiet", | ||
| 448 | (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL, | ||
| 449 | NULL, 0); | ||
| 450 | ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId); | ||
| 451 | |||
| 452 | LExit: | ||
| 453 | DependencyUninitialize(&dependencyProvider); | ||
| 454 | ReleaseStr(sczCachePath); | ||
| 455 | |||
| 456 | return hr; | ||
| 457 | } | ||
diff --git a/src/engine/relatedbundle.h b/src/engine/relatedbundle.h new file mode 100644 index 00000000..01691c25 --- /dev/null +++ b/src/engine/relatedbundle.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | HRESULT RelatedBundlesInitializeForScope( | ||
| 10 | __in BOOL fPerMachine, | ||
| 11 | __in BURN_REGISTRATION* pRegistration, | ||
| 12 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 13 | ); | ||
| 14 | void RelatedBundlesUninitialize( | ||
| 15 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
| 16 | ); | ||
| 17 | |||
| 18 | #if defined(__cplusplus) | ||
| 19 | } | ||
| 20 | #endif | ||
diff --git a/src/engine/search.cpp b/src/engine/search.cpp new file mode 100644 index 00000000..c50790fd --- /dev/null +++ b/src/engine/search.cpp | |||
| @@ -0,0 +1,1195 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | static HRESULT DirectorySearchExists( | ||
| 9 | __in BURN_SEARCH* pSearch, | ||
| 10 | __in BURN_VARIABLES* pVariables | ||
| 11 | ); | ||
| 12 | static HRESULT DirectorySearchPath( | ||
| 13 | __in BURN_SEARCH* pSearch, | ||
| 14 | __in BURN_VARIABLES* pVariables | ||
| 15 | ); | ||
| 16 | static HRESULT FileSearchExists( | ||
| 17 | __in BURN_SEARCH* pSearch, | ||
| 18 | __in BURN_VARIABLES* pVariables | ||
| 19 | ); | ||
| 20 | static HRESULT FileSearchVersion( | ||
| 21 | __in BURN_SEARCH* pSearch, | ||
| 22 | __in BURN_VARIABLES* pVariables | ||
| 23 | ); | ||
| 24 | static HRESULT FileSearchPath( | ||
| 25 | __in BURN_SEARCH* pSearch, | ||
| 26 | __in BURN_VARIABLES* pVariables | ||
| 27 | ); | ||
| 28 | static HRESULT RegistrySearchExists( | ||
| 29 | __in BURN_SEARCH* pSearch, | ||
| 30 | __in BURN_VARIABLES* pVariables | ||
| 31 | ); | ||
| 32 | static HRESULT RegistrySearchValue( | ||
| 33 | __in BURN_SEARCH* pSearch, | ||
| 34 | __in BURN_VARIABLES* pVariables | ||
| 35 | ); | ||
| 36 | static HRESULT MsiComponentSearch( | ||
| 37 | __in BURN_SEARCH* pSearch, | ||
| 38 | __in BURN_VARIABLES* pVariables | ||
| 39 | ); | ||
| 40 | static HRESULT MsiProductSearch( | ||
| 41 | __in BURN_SEARCH* pSearch, | ||
| 42 | __in BURN_VARIABLES* pVariables | ||
| 43 | ); | ||
| 44 | static HRESULT MsiFeatureSearch( | ||
| 45 | __in BURN_SEARCH* pSearch, | ||
| 46 | __in BURN_VARIABLES* pVariables | ||
| 47 | ); | ||
| 48 | |||
| 49 | |||
| 50 | // function definitions | ||
| 51 | |||
| 52 | extern "C" HRESULT SearchesParseFromXml( | ||
| 53 | __in BURN_SEARCHES* pSearches, | ||
| 54 | __in IXMLDOMNode* pixnBundle | ||
| 55 | ) | ||
| 56 | { | ||
| 57 | HRESULT hr = S_OK; | ||
| 58 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 59 | IXMLDOMNode* pixnNode = NULL; | ||
| 60 | DWORD cNodes = 0; | ||
| 61 | BSTR bstrNodeName = NULL; | ||
| 62 | LPWSTR scz = NULL; | ||
| 63 | |||
| 64 | // select search nodes | ||
| 65 | hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch", &pixnNodes); | ||
| 66 | ExitOnFailure(hr, "Failed to select search nodes."); | ||
| 67 | |||
| 68 | // get search node count | ||
| 69 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 70 | ExitOnFailure(hr, "Failed to get search node count."); | ||
| 71 | |||
| 72 | if (!cNodes) | ||
| 73 | { | ||
| 74 | ExitFunction(); | ||
| 75 | } | ||
| 76 | |||
| 77 | // allocate memory for searches | ||
| 78 | pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE); | ||
| 79 | ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); | ||
| 80 | |||
| 81 | pSearches->cSearches = cNodes; | ||
| 82 | |||
| 83 | // parse search elements | ||
| 84 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 85 | { | ||
| 86 | BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; | ||
| 87 | |||
| 88 | hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); | ||
| 89 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 90 | |||
| 91 | // @Id | ||
| 92 | hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey); | ||
| 93 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 94 | |||
| 95 | // @Variable | ||
| 96 | hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable); | ||
| 97 | ExitOnFailure(hr, "Failed to get @Variable."); | ||
| 98 | |||
| 99 | // @Condition | ||
| 100 | hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition); | ||
| 101 | if (E_NOTFOUND != hr) | ||
| 102 | { | ||
| 103 | ExitOnFailure(hr, "Failed to get @Condition."); | ||
| 104 | } | ||
| 105 | |||
| 106 | // read type specific attributes | ||
| 107 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1)) | ||
| 108 | { | ||
| 109 | pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY; | ||
| 110 | |||
| 111 | // @Path | ||
| 112 | hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath); | ||
| 113 | ExitOnFailure(hr, "Failed to get @Path."); | ||
| 114 | |||
| 115 | // @Type | ||
| 116 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 117 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 118 | |||
| 119 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) | ||
| 120 | { | ||
| 121 | pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS; | ||
| 122 | } | ||
| 123 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) | ||
| 124 | { | ||
| 125 | pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH; | ||
| 126 | } | ||
| 127 | else | ||
| 128 | { | ||
| 129 | hr = E_INVALIDARG; | ||
| 130 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1)) | ||
| 134 | { | ||
| 135 | pSearch->Type = BURN_SEARCH_TYPE_FILE; | ||
| 136 | |||
| 137 | // @Path | ||
| 138 | hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath); | ||
| 139 | ExitOnFailure(hr, "Failed to get @Path."); | ||
| 140 | |||
| 141 | // @Type | ||
| 142 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 143 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 144 | |||
| 145 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) | ||
| 146 | { | ||
| 147 | pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS; | ||
| 148 | } | ||
| 149 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) | ||
| 150 | { | ||
| 151 | pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION; | ||
| 152 | } | ||
| 153 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1)) | ||
| 154 | { | ||
| 155 | pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH; | ||
| 156 | } | ||
| 157 | else | ||
| 158 | { | ||
| 159 | hr = E_INVALIDARG; | ||
| 160 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1)) | ||
| 164 | { | ||
| 165 | pSearch->Type = BURN_SEARCH_TYPE_REGISTRY; | ||
| 166 | |||
| 167 | // @Root | ||
| 168 | hr = XmlGetAttributeEx(pixnNode, L"Root", &scz); | ||
| 169 | ExitOnFailure(hr, "Failed to get @Root."); | ||
| 170 | |||
| 171 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1)) | ||
| 172 | { | ||
| 173 | pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT; | ||
| 174 | } | ||
| 175 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1)) | ||
| 176 | { | ||
| 177 | pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER; | ||
| 178 | } | ||
| 179 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1)) | ||
| 180 | { | ||
| 181 | pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE; | ||
| 182 | } | ||
| 183 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1)) | ||
| 184 | { | ||
| 185 | pSearch->RegistrySearch.hRoot = HKEY_USERS; | ||
| 186 | } | ||
| 187 | else | ||
| 188 | { | ||
| 189 | hr = E_INVALIDARG; | ||
| 190 | ExitOnFailure(hr, "Invalid value for @Root: %ls", scz); | ||
| 191 | } | ||
| 192 | |||
| 193 | // @Key | ||
| 194 | hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey); | ||
| 195 | ExitOnFailure(hr, "Failed to get Key attribute."); | ||
| 196 | |||
| 197 | // @Value | ||
| 198 | hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue); | ||
| 199 | if (E_NOTFOUND != hr) | ||
| 200 | { | ||
| 201 | ExitOnFailure(hr, "Failed to get Value attribute."); | ||
| 202 | } | ||
| 203 | |||
| 204 | // @Type | ||
| 205 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 206 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 207 | |||
| 208 | hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64); | ||
| 209 | if (E_NOTFOUND != hr) | ||
| 210 | { | ||
| 211 | ExitOnFailure(hr, "Failed to get Win64 attribute."); | ||
| 212 | } | ||
| 213 | |||
| 214 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1)) | ||
| 215 | { | ||
| 216 | pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS; | ||
| 217 | } | ||
| 218 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1)) | ||
| 219 | { | ||
| 220 | pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE; | ||
| 221 | |||
| 222 | // @ExpandEnvironment | ||
| 223 | hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment); | ||
| 224 | if (E_NOTFOUND != hr) | ||
| 225 | { | ||
| 226 | ExitOnFailure(hr, "Failed to get @ExpandEnvironment."); | ||
| 227 | } | ||
| 228 | |||
| 229 | // @VariableType | ||
| 230 | hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz); | ||
| 231 | ExitOnFailure(hr, "Failed to get @VariableType."); | ||
| 232 | |||
| 233 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) | ||
| 234 | { | ||
| 235 | pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC; | ||
| 236 | } | ||
| 237 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) | ||
| 238 | { | ||
| 239 | pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING; | ||
| 240 | } | ||
| 241 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) | ||
| 242 | { | ||
| 243 | pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION; | ||
| 244 | } | ||
| 245 | else | ||
| 246 | { | ||
| 247 | hr = E_INVALIDARG; | ||
| 248 | ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | else | ||
| 252 | { | ||
| 253 | hr = E_INVALIDARG; | ||
| 254 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1)) | ||
| 258 | { | ||
| 259 | pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT; | ||
| 260 | |||
| 261 | // @ProductCode | ||
| 262 | hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode); | ||
| 263 | if (E_NOTFOUND != hr) | ||
| 264 | { | ||
| 265 | ExitOnFailure(hr, "Failed to get @ProductCode."); | ||
| 266 | } | ||
| 267 | |||
| 268 | // @ComponentId | ||
| 269 | hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId); | ||
| 270 | ExitOnFailure(hr, "Failed to get @ComponentId."); | ||
| 271 | |||
| 272 | // @Type | ||
| 273 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 274 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 275 | |||
| 276 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1)) | ||
| 277 | { | ||
| 278 | pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH; | ||
| 279 | } | ||
| 280 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) | ||
| 281 | { | ||
| 282 | pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE; | ||
| 283 | } | ||
| 284 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1)) | ||
| 285 | { | ||
| 286 | pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY; | ||
| 287 | } | ||
| 288 | else | ||
| 289 | { | ||
| 290 | hr = E_INVALIDARG; | ||
| 291 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1)) | ||
| 295 | { | ||
| 296 | pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT; | ||
| 297 | pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE; | ||
| 298 | |||
| 299 | // @ProductCode (if we don't find a product code then look for an upgrade code) | ||
| 300 | hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid); | ||
| 301 | if (E_NOTFOUND != hr) | ||
| 302 | { | ||
| 303 | ExitOnFailure(hr, "Failed to get @ProductCode."); | ||
| 304 | pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE; | ||
| 305 | } | ||
| 306 | else | ||
| 307 | { | ||
| 308 | // @UpgradeCode | ||
| 309 | hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid); | ||
| 310 | if (E_NOTFOUND != hr) | ||
| 311 | { | ||
| 312 | ExitOnFailure(hr, "Failed to get @UpgradeCode."); | ||
| 313 | pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | // make sure we found either a product or upgrade code | ||
| 318 | if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType) | ||
| 319 | { | ||
| 320 | hr = E_NOTFOUND; | ||
| 321 | ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode."); | ||
| 322 | } | ||
| 323 | |||
| 324 | // @Type | ||
| 325 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 326 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 327 | |||
| 328 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) | ||
| 329 | { | ||
| 330 | pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION; | ||
| 331 | } | ||
| 332 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1)) | ||
| 333 | { | ||
| 334 | pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE; | ||
| 335 | } | ||
| 336 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) | ||
| 337 | { | ||
| 338 | pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE; | ||
| 339 | } | ||
| 340 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1)) | ||
| 341 | { | ||
| 342 | pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT; | ||
| 343 | } | ||
| 344 | else | ||
| 345 | { | ||
| 346 | hr = E_INVALIDARG; | ||
| 347 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1)) | ||
| 351 | { | ||
| 352 | pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE; | ||
| 353 | |||
| 354 | // @ProductCode | ||
| 355 | hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode); | ||
| 356 | ExitOnFailure(hr, "Failed to get @ProductCode."); | ||
| 357 | |||
| 358 | // @FeatureId | ||
| 359 | hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId); | ||
| 360 | ExitOnFailure(hr, "Failed to get @FeatureId."); | ||
| 361 | |||
| 362 | // @Type | ||
| 363 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 364 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 365 | |||
| 366 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1)) | ||
| 367 | { | ||
| 368 | pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE; | ||
| 369 | } | ||
| 370 | else | ||
| 371 | { | ||
| 372 | hr = E_INVALIDARG; | ||
| 373 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | else | ||
| 377 | { | ||
| 378 | hr = E_UNEXPECTED; | ||
| 379 | ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); | ||
| 380 | } | ||
| 381 | |||
| 382 | // prepare next iteration | ||
| 383 | ReleaseNullObject(pixnNode); | ||
| 384 | ReleaseNullBSTR(bstrNodeName); | ||
| 385 | } | ||
| 386 | |||
| 387 | hr = S_OK; | ||
| 388 | |||
| 389 | LExit: | ||
| 390 | ReleaseObject(pixnNodes); | ||
| 391 | ReleaseObject(pixnNode); | ||
| 392 | ReleaseBSTR(bstrNodeName); | ||
| 393 | ReleaseStr(scz); | ||
| 394 | return hr; | ||
| 395 | } | ||
| 396 | |||
| 397 | extern "C" HRESULT SearchesExecute( | ||
| 398 | __in BURN_SEARCHES* pSearches, | ||
| 399 | __in BURN_VARIABLES* pVariables | ||
| 400 | ) | ||
| 401 | { | ||
| 402 | HRESULT hr = S_OK; | ||
| 403 | BOOL f = FALSE; | ||
| 404 | |||
| 405 | for (DWORD i = 0; i < pSearches->cSearches; ++i) | ||
| 406 | { | ||
| 407 | BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; | ||
| 408 | |||
| 409 | // evaluate condition | ||
| 410 | if (pSearch->sczCondition && *pSearch->sczCondition) | ||
| 411 | { | ||
| 412 | hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f); | ||
| 413 | if (E_INVALIDDATA == hr) | ||
| 414 | { | ||
| 415 | TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); | ||
| 416 | hr = S_OK; | ||
| 417 | continue; | ||
| 418 | } | ||
| 419 | ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition); | ||
| 420 | |||
| 421 | if (!f) | ||
| 422 | { | ||
| 423 | continue; // condition evaluated to false, skip | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | switch (pSearch->Type) | ||
| 428 | { | ||
| 429 | case BURN_SEARCH_TYPE_DIRECTORY: | ||
| 430 | switch (pSearch->DirectorySearch.Type) | ||
| 431 | { | ||
| 432 | case BURN_DIRECTORY_SEARCH_TYPE_EXISTS: | ||
| 433 | hr = DirectorySearchExists(pSearch, pVariables); | ||
| 434 | break; | ||
| 435 | case BURN_DIRECTORY_SEARCH_TYPE_PATH: | ||
| 436 | hr = DirectorySearchPath(pSearch, pVariables); | ||
| 437 | break; | ||
| 438 | default: | ||
| 439 | hr = E_UNEXPECTED; | ||
| 440 | } | ||
| 441 | break; | ||
| 442 | case BURN_SEARCH_TYPE_FILE: | ||
| 443 | switch (pSearch->FileSearch.Type) | ||
| 444 | { | ||
| 445 | case BURN_FILE_SEARCH_TYPE_EXISTS: | ||
| 446 | hr = FileSearchExists(pSearch, pVariables); | ||
| 447 | break; | ||
| 448 | case BURN_FILE_SEARCH_TYPE_VERSION: | ||
| 449 | hr = FileSearchVersion(pSearch, pVariables); | ||
| 450 | break; | ||
| 451 | case BURN_FILE_SEARCH_TYPE_PATH: | ||
| 452 | hr = FileSearchPath(pSearch, pVariables); | ||
| 453 | break; | ||
| 454 | default: | ||
| 455 | hr = E_UNEXPECTED; | ||
| 456 | } | ||
| 457 | break; | ||
| 458 | case BURN_SEARCH_TYPE_REGISTRY: | ||
| 459 | switch (pSearch->RegistrySearch.Type) | ||
| 460 | { | ||
| 461 | case BURN_REGISTRY_SEARCH_TYPE_EXISTS: | ||
| 462 | hr = RegistrySearchExists(pSearch, pVariables); | ||
| 463 | break; | ||
| 464 | case BURN_REGISTRY_SEARCH_TYPE_VALUE: | ||
| 465 | hr = RegistrySearchValue(pSearch, pVariables); | ||
| 466 | break; | ||
| 467 | default: | ||
| 468 | hr = E_UNEXPECTED; | ||
| 469 | } | ||
| 470 | break; | ||
| 471 | case BURN_SEARCH_TYPE_MSI_COMPONENT: | ||
| 472 | hr = MsiComponentSearch(pSearch, pVariables); | ||
| 473 | break; | ||
| 474 | case BURN_SEARCH_TYPE_MSI_PRODUCT: | ||
| 475 | hr = MsiProductSearch(pSearch, pVariables); | ||
| 476 | break; | ||
| 477 | case BURN_SEARCH_TYPE_MSI_FEATURE: | ||
| 478 | hr = MsiFeatureSearch(pSearch, pVariables); | ||
| 479 | break; | ||
| 480 | default: | ||
| 481 | hr = E_UNEXPECTED; | ||
| 482 | } | ||
| 483 | |||
| 484 | if (FAILED(hr)) | ||
| 485 | { | ||
| 486 | TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey); | ||
| 487 | continue; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | hr = S_OK; | ||
| 492 | |||
| 493 | LExit: | ||
| 494 | return hr; | ||
| 495 | } | ||
| 496 | |||
| 497 | extern "C" void SearchesUninitialize( | ||
| 498 | __in BURN_SEARCHES* pSearches | ||
| 499 | ) | ||
| 500 | { | ||
| 501 | if (pSearches->rgSearches) | ||
| 502 | { | ||
| 503 | for (DWORD i = 0; i < pSearches->cSearches; ++i) | ||
| 504 | { | ||
| 505 | BURN_SEARCH* pSearch = &pSearches->rgSearches[i]; | ||
| 506 | |||
| 507 | ReleaseStr(pSearch->sczKey); | ||
| 508 | ReleaseStr(pSearch->sczVariable); | ||
| 509 | ReleaseStr(pSearch->sczCondition); | ||
| 510 | |||
| 511 | switch (pSearch->Type) | ||
| 512 | { | ||
| 513 | case BURN_SEARCH_TYPE_DIRECTORY: | ||
| 514 | ReleaseStr(pSearch->DirectorySearch.sczPath); | ||
| 515 | break; | ||
| 516 | case BURN_SEARCH_TYPE_FILE: | ||
| 517 | ReleaseStr(pSearch->FileSearch.sczPath); | ||
| 518 | break; | ||
| 519 | case BURN_SEARCH_TYPE_REGISTRY: | ||
| 520 | ReleaseStr(pSearch->RegistrySearch.sczKey); | ||
| 521 | ReleaseStr(pSearch->RegistrySearch.sczValue); | ||
| 522 | break; | ||
| 523 | case BURN_SEARCH_TYPE_MSI_COMPONENT: | ||
| 524 | ReleaseStr(pSearch->MsiComponentSearch.sczProductCode); | ||
| 525 | ReleaseStr(pSearch->MsiComponentSearch.sczComponentId); | ||
| 526 | break; | ||
| 527 | case BURN_SEARCH_TYPE_MSI_PRODUCT: | ||
| 528 | ReleaseStr(pSearch->MsiProductSearch.sczGuid); | ||
| 529 | break; | ||
| 530 | case BURN_SEARCH_TYPE_MSI_FEATURE: | ||
| 531 | ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode); | ||
| 532 | ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId); | ||
| 533 | break; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | MemFree(pSearches->rgSearches); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | |||
| 541 | // internal function definitions | ||
| 542 | |||
| 543 | static HRESULT DirectorySearchExists( | ||
| 544 | __in BURN_SEARCH* pSearch, | ||
| 545 | __in BURN_VARIABLES* pVariables | ||
| 546 | ) | ||
| 547 | { | ||
| 548 | HRESULT hr = S_OK; | ||
| 549 | LPWSTR sczPath = NULL; | ||
| 550 | BOOL fExists = FALSE; | ||
| 551 | |||
| 552 | // format path | ||
| 553 | hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); | ||
| 554 | ExitOnFailure(hr, "Failed to format variable string."); | ||
| 555 | |||
| 556 | DWORD dwAttributes = ::GetFileAttributesW(sczPath); | ||
| 557 | if (INVALID_FILE_ATTRIBUTES == dwAttributes) | ||
| 558 | { | ||
| 559 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 560 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 561 | { | ||
| 562 | hr = S_OK; // didn't find file, fExists still is false. | ||
| 563 | } | ||
| 564 | } | ||
| 565 | else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) | ||
| 566 | { | ||
| 567 | fExists = TRUE; | ||
| 568 | } | ||
| 569 | |||
| 570 | // else must have found a file. | ||
| 571 | // What if there is a hidden variable in sczPath? | ||
| 572 | ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); | ||
| 573 | |||
| 574 | // set variable | ||
| 575 | hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); | ||
| 576 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 577 | |||
| 578 | LExit: | ||
| 579 | StrSecureZeroFreeString(sczPath); | ||
| 580 | |||
| 581 | return hr; | ||
| 582 | } | ||
| 583 | |||
| 584 | static HRESULT DirectorySearchPath( | ||
| 585 | __in BURN_SEARCH* pSearch, | ||
| 586 | __in BURN_VARIABLES* pVariables | ||
| 587 | ) | ||
| 588 | { | ||
| 589 | HRESULT hr = S_OK; | ||
| 590 | LPWSTR sczPath = NULL; | ||
| 591 | |||
| 592 | // format path | ||
| 593 | hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL); | ||
| 594 | ExitOnFailure(hr, "Failed to format variable string."); | ||
| 595 | |||
| 596 | DWORD dwAttributes = ::GetFileAttributesW(sczPath); | ||
| 597 | if (INVALID_FILE_ATTRIBUTES == dwAttributes) | ||
| 598 | { | ||
| 599 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 600 | } | ||
| 601 | else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) | ||
| 602 | { | ||
| 603 | hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); | ||
| 604 | ExitOnFailure(hr, "Failed to set directory search path variable."); | ||
| 605 | } | ||
| 606 | else // must have found a file. | ||
| 607 | { | ||
| 608 | hr = E_PATHNOTFOUND; | ||
| 609 | } | ||
| 610 | |||
| 611 | // What if there is a hidden variable in sczPath? | ||
| 612 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 613 | { | ||
| 614 | LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr); | ||
| 615 | ExitFunction1(hr = S_OK); | ||
| 616 | } | ||
| 617 | ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath); | ||
| 618 | |||
| 619 | LExit: | ||
| 620 | StrSecureZeroFreeString(sczPath); | ||
| 621 | |||
| 622 | return hr; | ||
| 623 | } | ||
| 624 | |||
| 625 | static HRESULT FileSearchExists( | ||
| 626 | __in BURN_SEARCH* pSearch, | ||
| 627 | __in BURN_VARIABLES* pVariables | ||
| 628 | ) | ||
| 629 | { | ||
| 630 | HRESULT hr = S_OK; | ||
| 631 | DWORD er = ERROR_SUCCESS; | ||
| 632 | LPWSTR sczPath = NULL; | ||
| 633 | BOOL fExists = FALSE; | ||
| 634 | |||
| 635 | // format path | ||
| 636 | hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); | ||
| 637 | ExitOnFailure(hr, "Failed to format variable string."); | ||
| 638 | |||
| 639 | // find file | ||
| 640 | DWORD dwAttributes = ::GetFileAttributesW(sczPath); | ||
| 641 | if (INVALID_FILE_ATTRIBUTES == dwAttributes) | ||
| 642 | { | ||
| 643 | er = ::GetLastError(); | ||
| 644 | if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) | ||
| 645 | { | ||
| 646 | // What if there is a hidden variable in sczPath? | ||
| 647 | LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); | ||
| 648 | } | ||
| 649 | else | ||
| 650 | { | ||
| 651 | ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath); | ||
| 652 | } | ||
| 653 | } | ||
| 654 | else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) | ||
| 655 | { | ||
| 656 | fExists = TRUE; | ||
| 657 | } | ||
| 658 | |||
| 659 | // set variable | ||
| 660 | hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); | ||
| 661 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 662 | |||
| 663 | LExit: | ||
| 664 | StrSecureZeroFreeString(sczPath); | ||
| 665 | return hr; | ||
| 666 | } | ||
| 667 | |||
| 668 | static HRESULT FileSearchVersion( | ||
| 669 | __in BURN_SEARCH* pSearch, | ||
| 670 | __in BURN_VARIABLES* pVariables | ||
| 671 | ) | ||
| 672 | { | ||
| 673 | HRESULT hr = S_OK; | ||
| 674 | ULARGE_INTEGER uliVersion = { }; | ||
| 675 | LPWSTR sczPath = NULL; | ||
| 676 | |||
| 677 | // format path | ||
| 678 | hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); | ||
| 679 | ExitOnFailure(hr, "Failed to format path string."); | ||
| 680 | |||
| 681 | // get file version | ||
| 682 | hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart); | ||
| 683 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 684 | { | ||
| 685 | // What if there is a hidden variable in sczPath? | ||
| 686 | LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); | ||
| 687 | ExitFunction1(hr = S_OK); | ||
| 688 | } | ||
| 689 | ExitOnFailure(hr, "Failed get file version."); | ||
| 690 | |||
| 691 | // set variable | ||
| 692 | hr = VariableSetVersion(pVariables, pSearch->sczVariable, uliVersion.QuadPart, FALSE); | ||
| 693 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 694 | |||
| 695 | LExit: | ||
| 696 | StrSecureZeroFreeString(sczPath); | ||
| 697 | return hr; | ||
| 698 | } | ||
| 699 | |||
| 700 | static HRESULT FileSearchPath( | ||
| 701 | __in BURN_SEARCH* pSearch, | ||
| 702 | __in BURN_VARIABLES* pVariables | ||
| 703 | ) | ||
| 704 | { | ||
| 705 | HRESULT hr = S_OK; | ||
| 706 | LPWSTR sczPath = NULL; | ||
| 707 | |||
| 708 | // format path | ||
| 709 | hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL); | ||
| 710 | ExitOnFailure(hr, "Failed to format variable string."); | ||
| 711 | |||
| 712 | DWORD dwAttributes = ::GetFileAttributesW(sczPath); | ||
| 713 | if (INVALID_FILE_ATTRIBUTES == dwAttributes) | ||
| 714 | { | ||
| 715 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 716 | } | ||
| 717 | else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory. | ||
| 718 | { | ||
| 719 | hr = E_FILENOTFOUND; | ||
| 720 | } | ||
| 721 | else // found our file. | ||
| 722 | { | ||
| 723 | hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); | ||
| 724 | ExitOnFailure(hr, "Failed to set variable to file search path."); | ||
| 725 | } | ||
| 726 | |||
| 727 | // What if there is a hidden variable in sczPath? | ||
| 728 | if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) | ||
| 729 | { | ||
| 730 | LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath); | ||
| 731 | ExitFunction1(hr = S_OK); | ||
| 732 | } | ||
| 733 | ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath); | ||
| 734 | |||
| 735 | LExit: | ||
| 736 | StrSecureZeroFreeString(sczPath); | ||
| 737 | |||
| 738 | return hr; | ||
| 739 | } | ||
| 740 | |||
| 741 | static HRESULT RegistrySearchExists( | ||
| 742 | __in BURN_SEARCH* pSearch, | ||
| 743 | __in BURN_VARIABLES* pVariables | ||
| 744 | ) | ||
| 745 | { | ||
| 746 | HRESULT hr = S_OK; | ||
| 747 | DWORD er = ERROR_SUCCESS; | ||
| 748 | LPWSTR sczKey = NULL; | ||
| 749 | LPWSTR sczValue = NULL; | ||
| 750 | HKEY hKey = NULL; | ||
| 751 | DWORD dwType = 0; | ||
| 752 | BOOL fExists = FALSE; | ||
| 753 | REGSAM samDesired = KEY_QUERY_VALUE; | ||
| 754 | |||
| 755 | if (pSearch->RegistrySearch.fWin64) | ||
| 756 | { | ||
| 757 | samDesired = samDesired | KEY_WOW64_64KEY; | ||
| 758 | } | ||
| 759 | |||
| 760 | // format key string | ||
| 761 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); | ||
| 762 | ExitOnFailure(hr, "Failed to format key string."); | ||
| 763 | |||
| 764 | // open key | ||
| 765 | hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); | ||
| 766 | if (SUCCEEDED(hr)) | ||
| 767 | { | ||
| 768 | fExists = TRUE; | ||
| 769 | } | ||
| 770 | else if (E_FILENOTFOUND == hr) | ||
| 771 | { | ||
| 772 | // What if there is a hidden variable in sczKey? | ||
| 773 | LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); | ||
| 774 | fExists = FALSE; | ||
| 775 | hr = S_OK; | ||
| 776 | } | ||
| 777 | else | ||
| 778 | { | ||
| 779 | // What if there is a hidden variable in sczKey? | ||
| 780 | ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey); | ||
| 781 | } | ||
| 782 | |||
| 783 | if (fExists && pSearch->RegistrySearch.sczValue) | ||
| 784 | { | ||
| 785 | // format value string | ||
| 786 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); | ||
| 787 | ExitOnFailure(hr, "Failed to format value string."); | ||
| 788 | |||
| 789 | // query value | ||
| 790 | er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL); | ||
| 791 | switch (er) | ||
| 792 | { | ||
| 793 | case ERROR_SUCCESS: | ||
| 794 | fExists = TRUE; | ||
| 795 | break; | ||
| 796 | case ERROR_FILE_NOT_FOUND: | ||
| 797 | // What if there is a hidden variable in sczKey or sczValue? | ||
| 798 | LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); | ||
| 799 | fExists = FALSE; | ||
| 800 | break; | ||
| 801 | default: | ||
| 802 | ExitOnWin32Error(er, hr, "Failed to query registry key value."); | ||
| 803 | } | ||
| 804 | } | ||
| 805 | |||
| 806 | // set variable | ||
| 807 | hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE); | ||
| 808 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 809 | |||
| 810 | LExit: | ||
| 811 | if (FAILED(hr)) | ||
| 812 | { | ||
| 813 | // What if there is a hidden variable in sczKey? | ||
| 814 | LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr); | ||
| 815 | } | ||
| 816 | |||
| 817 | StrSecureZeroFreeString(sczKey); | ||
| 818 | StrSecureZeroFreeString(sczValue); | ||
| 819 | ReleaseRegKey(hKey); | ||
| 820 | |||
| 821 | return hr; | ||
| 822 | } | ||
| 823 | |||
| 824 | static HRESULT RegistrySearchValue( | ||
| 825 | __in BURN_SEARCH* pSearch, | ||
| 826 | __in BURN_VARIABLES* pVariables | ||
| 827 | ) | ||
| 828 | { | ||
| 829 | HRESULT hr = S_OK; | ||
| 830 | DWORD er = ERROR_SUCCESS; | ||
| 831 | LPWSTR sczKey = NULL; | ||
| 832 | LPWSTR sczValue = NULL; | ||
| 833 | HKEY hKey = NULL; | ||
| 834 | DWORD dwType = 0; | ||
| 835 | DWORD cbData = 0; | ||
| 836 | LPBYTE pData = NULL; | ||
| 837 | DWORD cch = 0; | ||
| 838 | BURN_VARIANT value = { }; | ||
| 839 | REGSAM samDesired = KEY_QUERY_VALUE; | ||
| 840 | |||
| 841 | if (pSearch->RegistrySearch.fWin64) | ||
| 842 | { | ||
| 843 | samDesired = samDesired | KEY_WOW64_64KEY; | ||
| 844 | } | ||
| 845 | |||
| 846 | // format key string | ||
| 847 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL); | ||
| 848 | ExitOnFailure(hr, "Failed to format key string."); | ||
| 849 | |||
| 850 | // format value string | ||
| 851 | if (pSearch->RegistrySearch.sczValue) | ||
| 852 | { | ||
| 853 | hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL); | ||
| 854 | ExitOnFailure(hr, "Failed to format value string."); | ||
| 855 | } | ||
| 856 | |||
| 857 | // open key | ||
| 858 | hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey); | ||
| 859 | if (E_FILENOTFOUND == hr) | ||
| 860 | { | ||
| 861 | // What if there is a hidden variable in sczKey? | ||
| 862 | LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey); | ||
| 863 | hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); | ||
| 864 | ExitOnFailure(hr, "Failed to clear variable."); | ||
| 865 | ExitFunction1(hr = S_OK); | ||
| 866 | } | ||
| 867 | ExitOnFailure(hr, "Failed to open registry key."); | ||
| 868 | |||
| 869 | // get value | ||
| 870 | er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData); | ||
| 871 | if (ERROR_FILE_NOT_FOUND == er) | ||
| 872 | { | ||
| 873 | // What if there is a hidden variable in sczKey or sczValue? | ||
| 874 | LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue); | ||
| 875 | hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); | ||
| 876 | ExitOnFailure(hr, "Failed to clear variable."); | ||
| 877 | ExitFunction1(hr = S_OK); | ||
| 878 | } | ||
| 879 | ExitOnWin32Error(er, hr, "Failed to query registry key value size."); | ||
| 880 | |||
| 881 | pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ | ||
| 882 | ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value."); | ||
| 883 | |||
| 884 | er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData); | ||
| 885 | ExitOnWin32Error(er, hr, "Failed to query registry key value."); | ||
| 886 | |||
| 887 | switch (dwType) | ||
| 888 | { | ||
| 889 | case REG_DWORD: | ||
| 890 | if (sizeof(LONG) != cbData) | ||
| 891 | { | ||
| 892 | ExitFunction1(hr = E_UNEXPECTED); | ||
| 893 | } | ||
| 894 | hr = BVariantSetNumeric(&value, *((LONG*)pData)); | ||
| 895 | break; | ||
| 896 | case REG_QWORD: | ||
| 897 | if (sizeof(LONGLONG) != cbData) | ||
| 898 | { | ||
| 899 | ExitFunction1(hr = E_UNEXPECTED); | ||
| 900 | } | ||
| 901 | hr = BVariantSetNumeric(&value, *((LONGLONG*)pData)); | ||
| 902 | break; | ||
| 903 | case REG_EXPAND_SZ: | ||
| 904 | if (pSearch->RegistrySearch.fExpandEnvironment) | ||
| 905 | { | ||
| 906 | hr = StrAlloc(&value.sczValue, cbData); | ||
| 907 | ExitOnFailure(hr, "Failed to allocate string buffer."); | ||
| 908 | value.Type = BURN_VARIANT_TYPE_STRING; | ||
| 909 | |||
| 910 | cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData); | ||
| 911 | if (cch > cbData) | ||
| 912 | { | ||
| 913 | hr = StrAlloc(&value.sczValue, cch); | ||
| 914 | ExitOnFailure(hr, "Failed to allocate string buffer."); | ||
| 915 | |||
| 916 | if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch)) | ||
| 917 | { | ||
| 918 | ExitWithLastError(hr, "Failed to get expand environment string."); | ||
| 919 | } | ||
| 920 | } | ||
| 921 | break; | ||
| 922 | } | ||
| 923 | __fallthrough; | ||
| 924 | case REG_SZ: | ||
| 925 | hr = BVariantSetString(&value, (LPCWSTR)pData, 0); | ||
| 926 | break; | ||
| 927 | default: | ||
| 928 | ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType); | ||
| 929 | } | ||
| 930 | ExitOnFailure(hr, "Failed to read registry value."); | ||
| 931 | |||
| 932 | // change value to requested type | ||
| 933 | hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType); | ||
| 934 | ExitOnFailure(hr, "Failed to change value type."); | ||
| 935 | |||
| 936 | // Set variable as a literal. | ||
| 937 | hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); | ||
| 938 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 939 | |||
| 940 | LExit: | ||
| 941 | if (FAILED(hr)) | ||
| 942 | { | ||
| 943 | // What if there is a hidden variable in sczKey? | ||
| 944 | LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr); | ||
| 945 | } | ||
| 946 | |||
| 947 | StrSecureZeroFreeString(sczKey); | ||
| 948 | StrSecureZeroFreeString(sczValue); | ||
| 949 | ReleaseRegKey(hKey); | ||
| 950 | ReleaseMem(pData); | ||
| 951 | BVariantUninitialize(&value); | ||
| 952 | |||
| 953 | return hr; | ||
| 954 | } | ||
| 955 | |||
| 956 | static HRESULT MsiComponentSearch( | ||
| 957 | __in BURN_SEARCH* pSearch, | ||
| 958 | __in BURN_VARIABLES* pVariables | ||
| 959 | ) | ||
| 960 | { | ||
| 961 | HRESULT hr = S_OK; | ||
| 962 | INSTALLSTATE is = INSTALLSTATE_BROKEN; | ||
| 963 | LPWSTR sczComponentId = NULL; | ||
| 964 | LPWSTR sczProductCode = NULL; | ||
| 965 | LPWSTR sczPath = NULL; | ||
| 966 | |||
| 967 | // format component id string | ||
| 968 | hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL); | ||
| 969 | ExitOnFailure(hr, "Failed to format component id string."); | ||
| 970 | |||
| 971 | if (pSearch->MsiComponentSearch.sczProductCode) | ||
| 972 | { | ||
| 973 | // format product code string | ||
| 974 | hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL); | ||
| 975 | ExitOnFailure(hr, "Failed to format product code string."); | ||
| 976 | } | ||
| 977 | |||
| 978 | if (sczProductCode) | ||
| 979 | { | ||
| 980 | hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath); | ||
| 981 | } | ||
| 982 | else | ||
| 983 | { | ||
| 984 | hr = WiuLocateComponent(sczComponentId, &is, &sczPath); | ||
| 985 | } | ||
| 986 | |||
| 987 | if (INSTALLSTATE_SOURCEABSENT == is) | ||
| 988 | { | ||
| 989 | is = INSTALLSTATE_SOURCE; | ||
| 990 | } | ||
| 991 | else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is) | ||
| 992 | { | ||
| 993 | is = INSTALLSTATE_ABSENT; | ||
| 994 | } | ||
| 995 | else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is) | ||
| 996 | { | ||
| 997 | hr = E_INVALIDARG; | ||
| 998 | ExitOnFailure(hr, "Failed to get component path: %d", is); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | // set variable | ||
| 1002 | switch (pSearch->MsiComponentSearch.Type) | ||
| 1003 | { | ||
| 1004 | case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH: | ||
| 1005 | if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) | ||
| 1006 | { | ||
| 1007 | hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); | ||
| 1008 | } | ||
| 1009 | break; | ||
| 1010 | case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE: | ||
| 1011 | hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE); | ||
| 1012 | break; | ||
| 1013 | case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY: | ||
| 1014 | if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is) | ||
| 1015 | { | ||
| 1016 | // remove file part from path, if any | ||
| 1017 | LPWSTR wz = wcsrchr(sczPath, L'\\'); | ||
| 1018 | if (wz) | ||
| 1019 | { | ||
| 1020 | wz[1] = L'\0'; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE); | ||
| 1024 | } | ||
| 1025 | break; | ||
| 1026 | } | ||
| 1027 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 1028 | |||
| 1029 | LExit: | ||
| 1030 | if (FAILED(hr)) | ||
| 1031 | { | ||
| 1032 | LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | StrSecureZeroFreeString(sczComponentId); | ||
| 1036 | StrSecureZeroFreeString(sczProductCode); | ||
| 1037 | ReleaseStr(sczPath); | ||
| 1038 | return hr; | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | static HRESULT MsiProductSearch( | ||
| 1042 | __in BURN_SEARCH* pSearch, | ||
| 1043 | __in BURN_VARIABLES* pVariables | ||
| 1044 | ) | ||
| 1045 | { | ||
| 1046 | HRESULT hr = S_OK; | ||
| 1047 | LPWSTR sczGuid = NULL; | ||
| 1048 | LPCWSTR wzProperty = NULL; | ||
| 1049 | LPWSTR *rgsczRelatedProductCodes = NULL; | ||
| 1050 | DWORD dwRelatedProducts = 0; | ||
| 1051 | BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE; | ||
| 1052 | BURN_VARIANT value = { }; | ||
| 1053 | // We're not going to encrypt this value, so can access the value directly. | ||
| 1054 | |||
| 1055 | switch (pSearch->MsiProductSearch.Type) | ||
| 1056 | { | ||
| 1057 | case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: | ||
| 1058 | wzProperty = INSTALLPROPERTY_VERSIONSTRING; | ||
| 1059 | break; | ||
| 1060 | case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: | ||
| 1061 | wzProperty = INSTALLPROPERTY_LANGUAGE; | ||
| 1062 | break; | ||
| 1063 | case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: | ||
| 1064 | wzProperty = INSTALLPROPERTY_PRODUCTSTATE; | ||
| 1065 | break; | ||
| 1066 | case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: | ||
| 1067 | wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE; | ||
| 1068 | break; | ||
| 1069 | default: | ||
| 1070 | ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type); | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | // format guid string | ||
| 1074 | hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL); | ||
| 1075 | ExitOnFailure(hr, "Failed to format GUID string."); | ||
| 1076 | |||
| 1077 | // get product info | ||
| 1078 | value.Type = BURN_VARIANT_TYPE_STRING; | ||
| 1079 | |||
| 1080 | // if this is an upgrade code then get the product code of the highest versioned related product | ||
| 1081 | if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType) | ||
| 1082 | { | ||
| 1083 | // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there? | ||
| 1084 | hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE); | ||
| 1085 | ExitOnFailure(hr, "Failed to enumerate related products for upgrade code."); | ||
| 1086 | |||
| 1087 | // if we actually found a related product then use its upgrade code for the rest of the search | ||
| 1088 | if (1 == dwRelatedProducts) | ||
| 1089 | { | ||
| 1090 | hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0); | ||
| 1091 | ExitOnFailure(hr, "Failed to copy upgrade code."); | ||
| 1092 | } | ||
| 1093 | else | ||
| 1094 | { | ||
| 1095 | // set this here so we have a way of knowing that we don't need to bother | ||
| 1096 | // querying for the product information below | ||
| 1097 | hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT); | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr) | ||
| 1102 | { | ||
| 1103 | hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue); | ||
| 1104 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) | ||
| 1105 | { | ||
| 1106 | // product state is available only through MsiGetProductInfoEx | ||
| 1107 | // What if there is a hidden variable in sczGuid? | ||
| 1108 | LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid); | ||
| 1109 | hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue); | ||
| 1110 | |||
| 1111 | // if not in per-machine context, try per-user (unmanaged) | ||
| 1112 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) | ||
| 1113 | { | ||
| 1114 | // What if there is a hidden variable in sczGuid? | ||
| 1115 | LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid); | ||
| 1116 | hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue); | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) | ||
| 1122 | { | ||
| 1123 | // What if there is a hidden variable in sczGuid? | ||
| 1124 | LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid); | ||
| 1125 | |||
| 1126 | // set value to indicate absent | ||
| 1127 | switch (pSearch->MsiProductSearch.Type) | ||
| 1128 | { | ||
| 1129 | case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough; | ||
| 1130 | case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: | ||
| 1131 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1132 | value.llValue = 0; | ||
| 1133 | break; | ||
| 1134 | case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: | ||
| 1135 | // is supposed to remain empty | ||
| 1136 | break; | ||
| 1137 | case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: | ||
| 1138 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1139 | value.llValue = INSTALLSTATE_ABSENT; | ||
| 1140 | break; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | hr = S_OK; | ||
| 1144 | } | ||
| 1145 | ExitOnFailure(hr, "Failed to get product info."); | ||
| 1146 | |||
| 1147 | // change value type | ||
| 1148 | switch (pSearch->MsiProductSearch.Type) | ||
| 1149 | { | ||
| 1150 | case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION: | ||
| 1151 | type = BURN_VARIANT_TYPE_VERSION; | ||
| 1152 | break; | ||
| 1153 | case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE: | ||
| 1154 | type = BURN_VARIANT_TYPE_STRING; | ||
| 1155 | break; | ||
| 1156 | case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough; | ||
| 1157 | case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: | ||
| 1158 | type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1159 | break; | ||
| 1160 | } | ||
| 1161 | hr = BVariantChangeType(&value, type); | ||
| 1162 | ExitOnFailure(hr, "Failed to change value type."); | ||
| 1163 | |||
| 1164 | // Set variable as a literal. | ||
| 1165 | hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value); | ||
| 1166 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 1167 | |||
| 1168 | LExit: | ||
| 1169 | if (FAILED(hr)) | ||
| 1170 | { | ||
| 1171 | LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | StrSecureZeroFreeString(sczGuid); | ||
| 1175 | ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts); | ||
| 1176 | BVariantUninitialize(&value); | ||
| 1177 | |||
| 1178 | return hr; | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | static HRESULT MsiFeatureSearch( | ||
| 1182 | __in BURN_SEARCH* pSearch, | ||
| 1183 | __in BURN_VARIABLES* /*pVariables*/ | ||
| 1184 | ) | ||
| 1185 | { | ||
| 1186 | HRESULT hr = E_NOTIMPL; | ||
| 1187 | |||
| 1188 | //LExit: | ||
| 1189 | if (FAILED(hr)) | ||
| 1190 | { | ||
| 1191 | LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | return hr; | ||
| 1195 | } | ||
diff --git a/src/engine/search.h b/src/engine/search.h new file mode 100644 index 00000000..65dfb18f --- /dev/null +++ b/src/engine/search.h | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | enum BURN_SEARCH_TYPE | ||
| 13 | { | ||
| 14 | BURN_SEARCH_TYPE_NONE, | ||
| 15 | BURN_SEARCH_TYPE_DIRECTORY, | ||
| 16 | BURN_SEARCH_TYPE_FILE, | ||
| 17 | BURN_SEARCH_TYPE_REGISTRY, | ||
| 18 | BURN_SEARCH_TYPE_MSI_COMPONENT, | ||
| 19 | BURN_SEARCH_TYPE_MSI_PRODUCT, | ||
| 20 | BURN_SEARCH_TYPE_MSI_FEATURE, | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum BURN_DIRECTORY_SEARCH_TYPE | ||
| 24 | { | ||
| 25 | BURN_DIRECTORY_SEARCH_TYPE_NONE, | ||
| 26 | BURN_DIRECTORY_SEARCH_TYPE_EXISTS, | ||
| 27 | BURN_DIRECTORY_SEARCH_TYPE_PATH, | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum BURN_FILE_SEARCH_TYPE | ||
| 31 | { | ||
| 32 | BURN_FILE_SEARCH_TYPE_NONE, | ||
| 33 | BURN_FILE_SEARCH_TYPE_EXISTS, | ||
| 34 | BURN_FILE_SEARCH_TYPE_VERSION, | ||
| 35 | BURN_FILE_SEARCH_TYPE_PATH, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum BURN_REGISTRY_SEARCH_TYPE | ||
| 39 | { | ||
| 40 | BURN_REGISTRY_SEARCH_TYPE_NONE, | ||
| 41 | BURN_REGISTRY_SEARCH_TYPE_EXISTS, | ||
| 42 | BURN_REGISTRY_SEARCH_TYPE_VALUE, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum BURN_MSI_COMPONENT_SEARCH_TYPE | ||
| 46 | { | ||
| 47 | BURN_MSI_COMPONENT_SEARCH_TYPE_NONE, | ||
| 48 | BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH, | ||
| 49 | BURN_MSI_COMPONENT_SEARCH_TYPE_STATE, | ||
| 50 | BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY, | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum BURN_MSI_PRODUCT_SEARCH_TYPE | ||
| 54 | { | ||
| 55 | BURN_MSI_PRODUCT_SEARCH_TYPE_NONE, | ||
| 56 | BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION, | ||
| 57 | BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE, | ||
| 58 | BURN_MSI_PRODUCT_SEARCH_TYPE_STATE, | ||
| 59 | BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT, | ||
| 60 | }; | ||
| 61 | |||
| 62 | enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE | ||
| 63 | { | ||
| 64 | BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE, | ||
| 65 | BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE, | ||
| 66 | BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE | ||
| 67 | }; | ||
| 68 | |||
| 69 | enum BURN_MSI_FEATURE_SEARCH_TYPE | ||
| 70 | { | ||
| 71 | BURN_MSI_FEATURE_SEARCH_TYPE_NONE, | ||
| 72 | BURN_MSI_FEATURE_SEARCH_TYPE_STATE, | ||
| 73 | }; | ||
| 74 | |||
| 75 | |||
| 76 | // structs | ||
| 77 | |||
| 78 | typedef struct _BURN_SEARCH | ||
| 79 | { | ||
| 80 | LPWSTR sczKey; | ||
| 81 | LPWSTR sczVariable; | ||
| 82 | LPWSTR sczCondition; | ||
| 83 | |||
| 84 | BURN_SEARCH_TYPE Type; | ||
| 85 | union | ||
| 86 | { | ||
| 87 | struct | ||
| 88 | { | ||
| 89 | BURN_DIRECTORY_SEARCH_TYPE Type; | ||
| 90 | LPWSTR sczPath; | ||
| 91 | } DirectorySearch; | ||
| 92 | struct | ||
| 93 | { | ||
| 94 | BURN_FILE_SEARCH_TYPE Type; | ||
| 95 | LPWSTR sczPath; | ||
| 96 | } FileSearch; | ||
| 97 | struct | ||
| 98 | { | ||
| 99 | BURN_REGISTRY_SEARCH_TYPE Type; | ||
| 100 | BURN_VARIANT_TYPE VariableType; | ||
| 101 | HKEY hRoot; | ||
| 102 | LPWSTR sczKey; | ||
| 103 | LPWSTR sczValue; | ||
| 104 | BOOL fWin64; | ||
| 105 | BOOL fExpandEnvironment; | ||
| 106 | } RegistrySearch; | ||
| 107 | struct | ||
| 108 | { | ||
| 109 | BURN_MSI_COMPONENT_SEARCH_TYPE Type; | ||
| 110 | LPWSTR sczProductCode; | ||
| 111 | LPWSTR sczComponentId; | ||
| 112 | } MsiComponentSearch; | ||
| 113 | struct | ||
| 114 | { | ||
| 115 | BURN_MSI_PRODUCT_SEARCH_TYPE Type; | ||
| 116 | BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType; | ||
| 117 | LPWSTR sczGuid; | ||
| 118 | } MsiProductSearch; | ||
| 119 | struct | ||
| 120 | { | ||
| 121 | BURN_MSI_FEATURE_SEARCH_TYPE Type; | ||
| 122 | LPWSTR sczProductCode; | ||
| 123 | LPWSTR sczFeatureId; | ||
| 124 | } MsiFeatureSearch; | ||
| 125 | }; | ||
| 126 | } BURN_SEARCH; | ||
| 127 | |||
| 128 | typedef struct _BURN_SEARCHES | ||
| 129 | { | ||
| 130 | BURN_SEARCH* rgSearches; | ||
| 131 | DWORD cSearches; | ||
| 132 | } BURN_SEARCHES; | ||
| 133 | |||
| 134 | |||
| 135 | // function declarations | ||
| 136 | |||
| 137 | HRESULT SearchesParseFromXml( | ||
| 138 | __in BURN_SEARCHES* pSearches, | ||
| 139 | __in IXMLDOMNode* pixnBundle | ||
| 140 | ); | ||
| 141 | HRESULT SearchesExecute( | ||
| 142 | __in BURN_SEARCHES* pSearches, | ||
| 143 | __in BURN_VARIABLES* pVariables | ||
| 144 | ); | ||
| 145 | void SearchesUninitialize( | ||
| 146 | __in BURN_SEARCHES* pSearches | ||
| 147 | ); | ||
| 148 | |||
| 149 | |||
| 150 | #if defined(__cplusplus) | ||
| 151 | } | ||
| 152 | #endif | ||
diff --git a/src/engine/section.cpp b/src/engine/section.cpp new file mode 100644 index 00000000..3adcdd14 --- /dev/null +++ b/src/engine/section.cpp | |||
| @@ -0,0 +1,399 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // constants | ||
| 7 | |||
| 8 | // If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well. | ||
| 9 | #define BURN_SECTION_NAME ".wixburn" | ||
| 10 | #define BURN_SECTION_MAGIC 0x00f14300 | ||
| 11 | #define BURN_SECTION_VERSION 0x00000002 | ||
| 12 | #define MANIFEST_CABINET_TOKEN L"0" | ||
| 13 | |||
| 14 | // structs | ||
| 15 | typedef struct _BURN_SECTION_HEADER | ||
| 16 | { | ||
| 17 | DWORD dwMagic; | ||
| 18 | DWORD dwVersion; | ||
| 19 | |||
| 20 | GUID guidBundleId; | ||
| 21 | |||
| 22 | DWORD dwStubSize; | ||
| 23 | DWORD dwOriginalChecksum; | ||
| 24 | DWORD dwOriginalSignatureOffset; | ||
| 25 | DWORD dwOriginalSignatureSize; | ||
| 26 | |||
| 27 | DWORD dwFormat; | ||
| 28 | DWORD cContainers; | ||
| 29 | DWORD rgcbContainers[1]; | ||
| 30 | } BURN_SECTION_HEADER; | ||
| 31 | |||
| 32 | static HRESULT VerifySectionMatchesMemoryPEHeader( | ||
| 33 | __in REFGUID pSection | ||
| 34 | ); | ||
| 35 | |||
| 36 | |||
| 37 | extern "C" HRESULT SectionInitialize( | ||
| 38 | __in BURN_SECTION* pSection, | ||
| 39 | __in HANDLE hEngineFile, | ||
| 40 | __in HANDLE hSourceEngineFile | ||
| 41 | ) | ||
| 42 | { | ||
| 43 | HRESULT hr = S_OK; | ||
| 44 | DWORD cbRead = 0; | ||
| 45 | LARGE_INTEGER li = { }; | ||
| 46 | LONGLONG llSize = 0; | ||
| 47 | IMAGE_DOS_HEADER dosHeader = { }; | ||
| 48 | IMAGE_NT_HEADERS ntHeader = { }; | ||
| 49 | DWORD dwChecksumOffset = 0; | ||
| 50 | DWORD dwCertificateTableOffset = 0; | ||
| 51 | DWORD dwSignatureOffset = 0; | ||
| 52 | DWORD cbSignature = 0; | ||
| 53 | IMAGE_SECTION_HEADER sectionHeader = { }; | ||
| 54 | DWORD dwOriginalChecksumAndSignatureOffset = 0; | ||
| 55 | BURN_SECTION_HEADER* pBurnSectionHeader = NULL; | ||
| 56 | |||
| 57 | pSection->hEngineFile = hEngineFile; | ||
| 58 | ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path."); | ||
| 59 | |||
| 60 | pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile; | ||
| 61 | |||
| 62 | // | ||
| 63 | // First, make sure we have a valid DOS signature. | ||
| 64 | // | ||
| 65 | if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) | ||
| 66 | { | ||
| 67 | ExitWithLastError(hr, "Failed to seek to start of file."); | ||
| 68 | } | ||
| 69 | |||
| 70 | // read DOS header | ||
| 71 | if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL)) | ||
| 72 | { | ||
| 73 | ExitWithLastError(hr, "Failed to read DOS header."); | ||
| 74 | } | ||
| 75 | else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic) | ||
| 76 | { | ||
| 77 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 78 | ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); | ||
| 79 | } | ||
| 80 | |||
| 81 | // | ||
| 82 | // Now, make sure we have a valid NT signature. | ||
| 83 | // | ||
| 84 | |||
| 85 | // seek to new header | ||
| 86 | li.QuadPart = dosHeader.e_lfanew; | ||
| 87 | if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) | ||
| 88 | { | ||
| 89 | ExitWithLastError(hr, "Failed to seek to NT header."); | ||
| 90 | } | ||
| 91 | |||
| 92 | // read NT header | ||
| 93 | if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL)) | ||
| 94 | { | ||
| 95 | ExitWithLastError(hr, "Failed to read NT header."); | ||
| 96 | } | ||
| 97 | else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature) | ||
| 98 | { | ||
| 99 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 100 | ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Get the table offsets. | ||
| 104 | dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16); | ||
| 105 | dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); | ||
| 106 | |||
| 107 | // Seek into the certificate table to get the signature size. | ||
| 108 | li.QuadPart = dwCertificateTableOffset; | ||
| 109 | if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) | ||
| 110 | { | ||
| 111 | ExitWithLastError(hr, "Failed to seek to section info."); | ||
| 112 | } | ||
| 113 | |||
| 114 | if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL)) | ||
| 115 | { | ||
| 116 | ExitWithLastError(hr, "Failed to read signature offset."); | ||
| 117 | } | ||
| 118 | |||
| 119 | if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL)) | ||
| 120 | { | ||
| 121 | ExitWithLastError(hr, "Failed to read signature size."); | ||
| 122 | } | ||
| 123 | |||
| 124 | // | ||
| 125 | // Finally, get into the section table and look for the Burn section info. | ||
| 126 | // | ||
| 127 | |||
| 128 | // seek past optional headers | ||
| 129 | li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader; | ||
| 130 | if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) | ||
| 131 | { | ||
| 132 | ExitWithLastError(hr, "Failed to seek past optional headers."); | ||
| 133 | } | ||
| 134 | |||
| 135 | // read sections one by one until we find our section | ||
| 136 | for (DWORD i = 0; ; ++i) | ||
| 137 | { | ||
| 138 | // read section | ||
| 139 | if (!::ReadFile(pSection->hEngineFile, §ionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL)) | ||
| 140 | { | ||
| 141 | ExitWithLastError(hr, "Failed to read image section header, index: %u", i); | ||
| 142 | } | ||
| 143 | if (sizeof(IMAGE_SECTION_HEADER) > cbRead) | ||
| 144 | { | ||
| 145 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 146 | ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i); | ||
| 147 | } | ||
| 148 | |||
| 149 | // compare header name | ||
| 150 | C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1); | ||
| 151 | if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name))) | ||
| 152 | { | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | |||
| 156 | // fail if we hit the end | ||
| 157 | if (i + 1 >= ntHeader.FileHeader.NumberOfSections) | ||
| 158 | { | ||
| 159 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 160 | ExitOnRootFailure(hr, "Failed to find Burn section."); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | // | ||
| 165 | // We've arrived at the section info. | ||
| 166 | // | ||
| 167 | |||
| 168 | // check size of section | ||
| 169 | if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData) | ||
| 170 | { | ||
| 171 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 172 | ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData); | ||
| 173 | } | ||
| 174 | |||
| 175 | // allocate buffer for section info | ||
| 176 | pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE); | ||
| 177 | ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info."); | ||
| 178 | |||
| 179 | // seek to section info | ||
| 180 | li.QuadPart = sectionHeader.PointerToRawData; | ||
| 181 | if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN)) | ||
| 182 | { | ||
| 183 | ExitWithLastError(hr, "Failed to seek to section info."); | ||
| 184 | } | ||
| 185 | |||
| 186 | // Note the location of original checksum and signature information in the burn section header. | ||
| 187 | dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast<LPBYTE>(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast<LPBYTE>(pBurnSectionHeader)); | ||
| 188 | |||
| 189 | // read section info | ||
| 190 | if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL)) | ||
| 191 | { | ||
| 192 | ExitWithLastError(hr, "Failed to read section info."); | ||
| 193 | } | ||
| 194 | else if (sectionHeader.SizeOfRawData > cbRead) | ||
| 195 | { | ||
| 196 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 197 | ExitOnRootFailure(hr, "Failed to read complete section info."); | ||
| 198 | } | ||
| 199 | |||
| 200 | // validate version of section info | ||
| 201 | if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) | ||
| 202 | { | ||
| 203 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 204 | ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); | ||
| 205 | } | ||
| 206 | |||
| 207 | hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize); | ||
| 208 | ExitOnFailure(hr, "Failed to get total size of bundle."); | ||
| 209 | |||
| 210 | pSection->cbStub = pBurnSectionHeader->dwStubSize; | ||
| 211 | |||
| 212 | // If there is an original signature use that to determine the engine size. | ||
| 213 | if (pBurnSectionHeader->dwOriginalSignatureOffset) | ||
| 214 | { | ||
| 215 | pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize; | ||
| 216 | } | ||
| 217 | else if (dwSignatureOffset) // if there is a signature, use it. | ||
| 218 | { | ||
| 219 | pSection->cbEngineSize = dwSignatureOffset + cbSignature; | ||
| 220 | } | ||
| 221 | else // just use the stub and UX container as the size of the engine. | ||
| 222 | { | ||
| 223 | pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0]; | ||
| 224 | } | ||
| 225 | |||
| 226 | pSection->qwBundleSize = static_cast<DWORD64>(llSize); | ||
| 227 | |||
| 228 | pSection->dwChecksumOffset = dwChecksumOffset; | ||
| 229 | pSection->dwCertificateTableOffset = dwCertificateTableOffset; | ||
| 230 | pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset; | ||
| 231 | |||
| 232 | pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum; | ||
| 233 | pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset; | ||
| 234 | pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize; | ||
| 235 | |||
| 236 | pSection->dwFormat = pBurnSectionHeader->dwFormat; | ||
| 237 | pSection->cContainers = pBurnSectionHeader->cContainers; | ||
| 238 | pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE); | ||
| 239 | ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes."); | ||
| 240 | |||
| 241 | memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers); | ||
| 242 | |||
| 243 | // TODO: verify more than just the GUID. | ||
| 244 | hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId); | ||
| 245 | ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory."); | ||
| 246 | |||
| 247 | LExit: | ||
| 248 | ReleaseMem(pBurnSectionHeader); | ||
| 249 | |||
| 250 | return hr; | ||
| 251 | } | ||
| 252 | |||
| 253 | extern "C" void SectionUninitialize( | ||
| 254 | __out BURN_SECTION* pSection | ||
| 255 | ) | ||
| 256 | { | ||
| 257 | ReleaseMem(pSection->rgcbContainers); | ||
| 258 | memset(pSection, 0, sizeof(BURN_SECTION)); | ||
| 259 | } | ||
| 260 | |||
| 261 | extern "C" HRESULT SectionGetAttachedContainerInfo( | ||
| 262 | __in BURN_SECTION* pSection, | ||
| 263 | __in DWORD iContainerIndex, | ||
| 264 | __in DWORD dwExpectedType, | ||
| 265 | __out DWORD64* pqwOffset, | ||
| 266 | __out DWORD64* pqwSize, | ||
| 267 | __out BOOL* pfPresent | ||
| 268 | ) | ||
| 269 | { | ||
| 270 | HRESULT hr = S_OK; | ||
| 271 | |||
| 272 | // validate container info | ||
| 273 | if (iContainerIndex >= pSection->cContainers) | ||
| 274 | { | ||
| 275 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 276 | ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers); | ||
| 277 | } | ||
| 278 | else if (dwExpectedType != pSection->dwFormat) | ||
| 279 | { | ||
| 280 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 281 | ExitOnRootFailure(hr, "Unexpected container format."); | ||
| 282 | } | ||
| 283 | |||
| 284 | // If we are asking for the UX container, find it right after the stub. | ||
| 285 | if (0 == iContainerIndex) | ||
| 286 | { | ||
| 287 | *pqwOffset = pSection->cbStub; | ||
| 288 | } | ||
| 289 | else // attached containers start after the whole engine. | ||
| 290 | { | ||
| 291 | *pqwOffset = pSection->cbEngineSize; | ||
| 292 | for (DWORD i = 1; i < iContainerIndex; ++i) | ||
| 293 | { | ||
| 294 | *pqwOffset += pSection->rgcbContainers[i]; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | *pqwSize = pSection->rgcbContainers[iContainerIndex]; | ||
| 299 | *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize; | ||
| 300 | |||
| 301 | AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong."); | ||
| 302 | |||
| 303 | LExit: | ||
| 304 | return hr; | ||
| 305 | } | ||
| 306 | |||
| 307 | HRESULT VerifySectionMatchesMemoryPEHeader( | ||
| 308 | __in REFGUID pBundleId | ||
| 309 | ) | ||
| 310 | { | ||
| 311 | HRESULT hr = S_OK; | ||
| 312 | BYTE* pbPEHeader = NULL; | ||
| 313 | PIMAGE_DOS_HEADER pDosHeader = NULL; | ||
| 314 | PIMAGE_NT_HEADERS pNtHeader = NULL; | ||
| 315 | PIMAGE_SECTION_HEADER pSections = NULL; | ||
| 316 | PIMAGE_SECTION_HEADER pSectionHeader = NULL; | ||
| 317 | BURN_SECTION_HEADER* pBurnSectionHeader = NULL; | ||
| 318 | |||
| 319 | pbPEHeader = reinterpret_cast<BYTE*>(::GetModuleHandleW(NULL)); | ||
| 320 | ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process."); | ||
| 321 | |||
| 322 | // | ||
| 323 | // First, make sure we have a valid DOS signature. | ||
| 324 | // | ||
| 325 | |||
| 326 | pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbPEHeader); | ||
| 327 | if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) | ||
| 328 | { | ||
| 329 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 330 | ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer."); | ||
| 331 | } | ||
| 332 | |||
| 333 | // | ||
| 334 | // Now, make sure we have a valid NT signature. | ||
| 335 | // | ||
| 336 | |||
| 337 | pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pbPEHeader + pDosHeader->e_lfanew); | ||
| 338 | if (IMAGE_NT_SIGNATURE != pNtHeader->Signature) | ||
| 339 | { | ||
| 340 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 341 | ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer."); | ||
| 342 | } | ||
| 343 | |||
| 344 | // | ||
| 345 | // Finally, get into the section table and look for the Burn section info. | ||
| 346 | // | ||
| 347 | |||
| 348 | pSections = reinterpret_cast<PIMAGE_SECTION_HEADER>(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader); | ||
| 349 | |||
| 350 | // Read sections one by one until we find our section. | ||
| 351 | for (DWORD i = 0; ; ++i) | ||
| 352 | { | ||
| 353 | pSectionHeader = pSections + i; | ||
| 354 | |||
| 355 | // Compare header name. | ||
| 356 | C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1); | ||
| 357 | if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name))) | ||
| 358 | { | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | |||
| 362 | // Fail if we hit the end. | ||
| 363 | if (i + 1 >= pNtHeader->FileHeader.NumberOfSections) | ||
| 364 | { | ||
| 365 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 366 | ExitOnRootFailure(hr, "Failed to find Burn section."); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | // | ||
| 371 | // We've arrived at the section info. | ||
| 372 | // | ||
| 373 | |||
| 374 | // Check size of section. | ||
| 375 | if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData) | ||
| 376 | { | ||
| 377 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 378 | ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData); | ||
| 379 | } | ||
| 380 | |||
| 381 | // Get Burn section info. | ||
| 382 | pBurnSectionHeader = reinterpret_cast<BURN_SECTION_HEADER*>(pbPEHeader + pSectionHeader->VirtualAddress); | ||
| 383 | |||
| 384 | // Validate version of section info. | ||
| 385 | if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) | ||
| 386 | { | ||
| 387 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
| 388 | ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); | ||
| 389 | } | ||
| 390 | |||
| 391 | if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId)) | ||
| 392 | { | ||
| 393 | hr = E_INVALIDDATA; | ||
| 394 | ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory."); | ||
| 395 | } | ||
| 396 | |||
| 397 | LExit: | ||
| 398 | return hr; | ||
| 399 | } | ||
diff --git a/src/engine/section.h b/src/engine/section.h new file mode 100644 index 00000000..78331469 --- /dev/null +++ b/src/engine/section.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // structs | ||
| 11 | |||
| 12 | typedef struct _BURN_SECTION | ||
| 13 | { | ||
| 14 | HANDLE hEngineFile; | ||
| 15 | HANDLE hSourceEngineFile; | ||
| 16 | |||
| 17 | DWORD cbStub; | ||
| 18 | DWORD cbEngineSize; // stub + UX container + original certficiate | ||
| 19 | DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate] | ||
| 20 | |||
| 21 | DWORD dwChecksumOffset; | ||
| 22 | DWORD dwCertificateTableOffset; | ||
| 23 | DWORD dwOriginalChecksumAndSignatureOffset; | ||
| 24 | |||
| 25 | DWORD dwOriginalChecksum; | ||
| 26 | DWORD dwOriginalSignatureOffset; | ||
| 27 | DWORD dwOriginalSignatureSize; | ||
| 28 | |||
| 29 | DWORD dwFormat; | ||
| 30 | DWORD cContainers; | ||
| 31 | DWORD* rgcbContainers; | ||
| 32 | } BURN_SECTION; | ||
| 33 | |||
| 34 | |||
| 35 | HRESULT SectionInitialize( | ||
| 36 | __in BURN_SECTION* pSection, | ||
| 37 | __in HANDLE hEngineFile, | ||
| 38 | __in HANDLE hSourceEngineFile | ||
| 39 | ); | ||
| 40 | void SectionUninitialize( | ||
| 41 | __in BURN_SECTION* pSection | ||
| 42 | ); | ||
| 43 | HRESULT SectionGetAttachedContainerInfo( | ||
| 44 | __in BURN_SECTION* pSection, | ||
| 45 | __in DWORD iContainerIndex, | ||
| 46 | __in DWORD dwExpectedType, | ||
| 47 | __out DWORD64* pqwOffset, | ||
| 48 | __out DWORD64* pqwSize, | ||
| 49 | __out BOOL* pfPresent | ||
| 50 | ); | ||
| 51 | |||
| 52 | #if defined(__cplusplus) | ||
| 53 | } | ||
| 54 | #endif | ||
diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp new file mode 100644 index 00000000..1f95886a --- /dev/null +++ b/src/engine/splashscreen.cpp | |||
| @@ -0,0 +1,316 @@ | |||
| 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 | |||
| 5 | using namespace Gdiplus; | ||
| 6 | |||
| 7 | #define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" | ||
| 8 | #define IDB_SPLASHSCREEN 1 | ||
| 9 | |||
| 10 | // struct | ||
| 11 | |||
| 12 | struct SPLASHSCREEN_INFO | ||
| 13 | { | ||
| 14 | Bitmap* pBitmap; | ||
| 15 | Point pt; | ||
| 16 | Size size; | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct SPLASHSCREEN_CONTEXT | ||
| 20 | { | ||
| 21 | HANDLE hIntializedEvent; | ||
| 22 | HINSTANCE hInstance; | ||
| 23 | LPCWSTR wzCaption; | ||
| 24 | |||
| 25 | HWND* pHwnd; | ||
| 26 | }; | ||
| 27 | |||
| 28 | // internal function definitions | ||
| 29 | |||
| 30 | static DWORD WINAPI ThreadProc( | ||
| 31 | __in LPVOID pvContext | ||
| 32 | ); | ||
| 33 | static LRESULT CALLBACK WndProc( | ||
| 34 | __in HWND hWnd, | ||
| 35 | __in UINT uMsg, | ||
| 36 | __in WPARAM wParam, | ||
| 37 | __in LPARAM lParam | ||
| 38 | ); | ||
| 39 | static void OnPaint( | ||
| 40 | __in HDC hdc, | ||
| 41 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 42 | ); | ||
| 43 | static HRESULT LoadSplashScreen( | ||
| 44 | __in HMODULE hInstance, | ||
| 45 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 46 | ); | ||
| 47 | |||
| 48 | |||
| 49 | // function definitions | ||
| 50 | |||
| 51 | extern "C" void SplashScreenCreate( | ||
| 52 | __in HINSTANCE hInstance, | ||
| 53 | __in_z_opt LPCWSTR wzCaption, | ||
| 54 | __out HWND* pHwnd | ||
| 55 | ) | ||
| 56 | { | ||
| 57 | HRESULT hr = S_OK; | ||
| 58 | SPLASHSCREEN_CONTEXT context = { }; | ||
| 59 | HANDLE rgSplashScreenEvents[2] = { }; | ||
| 60 | DWORD dwSplashScreenThreadId = 0; | ||
| 61 | |||
| 62 | rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 63 | ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event."); | ||
| 64 | |||
| 65 | // create splash screen thread. | ||
| 66 | context.hIntializedEvent = rgSplashScreenEvents[0]; | ||
| 67 | context.hInstance = hInstance; | ||
| 68 | context.wzCaption = wzCaption; | ||
| 69 | context.pHwnd = pHwnd; | ||
| 70 | |||
| 71 | rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId); | ||
| 72 | ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread."); | ||
| 73 | |||
| 74 | // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits | ||
| 75 | // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two | ||
| 76 | // events to happen. | ||
| 77 | ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE); | ||
| 78 | |||
| 79 | LExit: | ||
| 80 | ReleaseHandle(rgSplashScreenEvents[1]); | ||
| 81 | ReleaseHandle(rgSplashScreenEvents[0]); | ||
| 82 | } | ||
| 83 | |||
| 84 | extern "C" HRESULT SplashScreenDisplayError( | ||
| 85 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 86 | __in_z LPCWSTR wzBundleName, | ||
| 87 | __in HRESULT hrError | ||
| 88 | ) | ||
| 89 | { | ||
| 90 | HRESULT hr = S_OK; | ||
| 91 | LPWSTR sczDisplayString = NULL; | ||
| 92 | |||
| 93 | hr = StrAllocFromError(&sczDisplayString, hrError, NULL); | ||
| 94 | ExitOnFailure(hr, "Failed to allocate string to display error message"); | ||
| 95 | |||
| 96 | Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString); | ||
| 97 | |||
| 98 | if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display) | ||
| 99 | { | ||
| 100 | // Don't display the error dialog in these modes | ||
| 101 | ExitFunction1(hr = S_OK); | ||
| 102 | } | ||
| 103 | |||
| 104 | ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); | ||
| 105 | |||
| 106 | LExit: | ||
| 107 | ReleaseStr(sczDisplayString); | ||
| 108 | |||
| 109 | return hr; | ||
| 110 | } | ||
| 111 | |||
| 112 | |||
| 113 | static DWORD WINAPI ThreadProc( | ||
| 114 | __in LPVOID pvContext | ||
| 115 | ) | ||
| 116 | { | ||
| 117 | HRESULT hr = S_OK; | ||
| 118 | |||
| 119 | ULONG_PTR token = 0; | ||
| 120 | GdiplusStartupInput input; | ||
| 121 | GdiplusStartupOutput output = { }; | ||
| 122 | |||
| 123 | SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext); | ||
| 124 | SPLASHSCREEN_INFO splashScreen = { }; | ||
| 125 | |||
| 126 | WNDCLASSW wc = { }; | ||
| 127 | BOOL fRegistered = TRUE; | ||
| 128 | HWND hWnd = NULL; | ||
| 129 | |||
| 130 | BOOL fRet = FALSE; | ||
| 131 | MSG msg = { }; | ||
| 132 | |||
| 133 | input.GdiplusVersion = 1; | ||
| 134 | |||
| 135 | hr = GdipInitialize(&input, &token, &output); | ||
| 136 | ExitOnFailure(hr, "Failed to initialize GDI+."); | ||
| 137 | |||
| 138 | hr = LoadSplashScreen(pContext->hInstance, &splashScreen); | ||
| 139 | ExitOnFailure(hr, "Failed to load splash screen."); | ||
| 140 | |||
| 141 | // Register the window class and create the window. | ||
| 142 | wc.lpfnWndProc = WndProc; | ||
| 143 | wc.hInstance = pContext->hInstance; | ||
| 144 | wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); | ||
| 145 | wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW; | ||
| 146 | if (!::RegisterClassW(&wc)) | ||
| 147 | { | ||
| 148 | ExitWithLastError(hr, "Failed to register window."); | ||
| 149 | } | ||
| 150 | |||
| 151 | fRegistered = TRUE; | ||
| 152 | |||
| 153 | hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen); | ||
| 154 | ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); | ||
| 155 | |||
| 156 | // Return the splash screen window and free the main thread waiting for us to be initialized. | ||
| 157 | *pContext->pHwnd = hWnd; | ||
| 158 | ::SetEvent(pContext->hIntializedEvent); | ||
| 159 | |||
| 160 | // Pump messages until the bootstrapper application destroys the window. | ||
| 161 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
| 162 | { | ||
| 163 | if (-1 == fRet) | ||
| 164 | { | ||
| 165 | hr = E_UNEXPECTED; | ||
| 166 | ExitOnFailure(hr, "Unexpected return value from message pump."); | ||
| 167 | } | ||
| 168 | else if (!::IsDialogMessageW(hWnd, &msg)) | ||
| 169 | { | ||
| 170 | ::TranslateMessage(&msg); | ||
| 171 | ::DispatchMessageW(&msg); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | LExit: | ||
| 176 | if (fRegistered) | ||
| 177 | { | ||
| 178 | ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance); | ||
| 179 | } | ||
| 180 | |||
| 181 | if (splashScreen.pBitmap) | ||
| 182 | { | ||
| 183 | delete splashScreen.pBitmap; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (token) | ||
| 187 | { | ||
| 188 | GdipUninitialize(token); | ||
| 189 | } | ||
| 190 | |||
| 191 | return hr; | ||
| 192 | } | ||
| 193 | |||
| 194 | static LRESULT CALLBACK WndProc( | ||
| 195 | __in HWND hWnd, | ||
| 196 | __in UINT uMsg, | ||
| 197 | __in WPARAM wParam, | ||
| 198 | __in LPARAM lParam | ||
| 199 | ) | ||
| 200 | { | ||
| 201 | LRESULT lres = 0; | ||
| 202 | SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA)); | ||
| 203 | |||
| 204 | switch (uMsg) | ||
| 205 | { | ||
| 206 | case WM_NCCREATE: | ||
| 207 | { | ||
| 208 | LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam); | ||
| 209 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams)); | ||
| 210 | } | ||
| 211 | break; | ||
| 212 | |||
| 213 | case WM_NCDESTROY: | ||
| 214 | lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 215 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); | ||
| 216 | return lres; | ||
| 217 | |||
| 218 | case WM_NCHITTEST: | ||
| 219 | return HTCAPTION; // allow window to be moved by grabbing any pixel. | ||
| 220 | |||
| 221 | case WM_DESTROY: | ||
| 222 | ::PostQuitMessage(0); | ||
| 223 | return 0; | ||
| 224 | |||
| 225 | case WM_ERASEBKGND: | ||
| 226 | // The splash screen image will be repainted in its entirety. | ||
| 227 | return 1; | ||
| 228 | |||
| 229 | case WM_PAINT: | ||
| 230 | { | ||
| 231 | PAINTSTRUCT ps = { }; | ||
| 232 | |||
| 233 | HDC hdc = BeginPaint(hWnd, &ps); | ||
| 234 | OnPaint(hdc, pSplashScreen); | ||
| 235 | EndPaint(hWnd, &ps); | ||
| 236 | } | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 241 | } | ||
| 242 | |||
| 243 | static void OnPaint( | ||
| 244 | __in HDC hdc, | ||
| 245 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 246 | ) | ||
| 247 | { | ||
| 248 | // Use high-quality bicubuc stretching from GDI+ which looks better than GDI. | ||
| 249 | Graphics graphics(hdc); | ||
| 250 | graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); | ||
| 251 | |||
| 252 | Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height); | ||
| 253 | Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst); | ||
| 254 | |||
| 255 | #if DEBUG | ||
| 256 | HRESULT hr = GdipHresultFromStatus(status); | ||
| 257 | TraceError(hr, "Failed to draw splash screen bitmap."); | ||
| 258 | #else | ||
| 259 | UNREFERENCED_PARAMETER(status); | ||
| 260 | #endif | ||
| 261 | } | ||
| 262 | |||
| 263 | static HRESULT LoadSplashScreen( | ||
| 264 | __in HMODULE hInstance, | ||
| 265 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
| 266 | ) | ||
| 267 | { | ||
| 268 | HRESULT hr = S_OK; | ||
| 269 | POINT ptCursor = { }; | ||
| 270 | HMONITOR hMonitor = NULL; | ||
| 271 | MONITORINFOEXW mi; | ||
| 272 | HDC hdc = NULL; | ||
| 273 | UINT dpiX = 0; | ||
| 274 | UINT dpiY = 0; | ||
| 275 | |||
| 276 | pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); | ||
| 277 | ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap."); | ||
| 278 | ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap."); | ||
| 279 | |||
| 280 | pSplashScreen->pt.X = CW_USEDEFAULT; | ||
| 281 | pSplashScreen->pt.Y = CW_USEDEFAULT; | ||
| 282 | pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth(); | ||
| 283 | pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight(); | ||
| 284 | |||
| 285 | // Stretch and center the window on the monitor with the mouse. | ||
| 286 | if (::GetCursorPos(&ptCursor)) | ||
| 287 | { | ||
| 288 | hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); | ||
| 289 | if (hMonitor) | ||
| 290 | { | ||
| 291 | ZeroMemory(&mi, sizeof(mi)); | ||
| 292 | mi.cbSize = sizeof(mi); | ||
| 293 | |||
| 294 | if (::GetMonitorInfoW(hMonitor, &mi)) | ||
| 295 | { | ||
| 296 | hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); | ||
| 297 | if (hdc) | ||
| 298 | { | ||
| 299 | dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); | ||
| 300 | dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); | ||
| 301 | |||
| 302 | pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96; | ||
| 303 | pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96; | ||
| 304 | |||
| 305 | ::ReleaseDC(NULL, hdc); | ||
| 306 | } | ||
| 307 | |||
| 308 | pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2; | ||
| 309 | pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | LExit: | ||
| 315 | return hr; | ||
| 316 | } | ||
diff --git a/src/engine/splashscreen.h b/src/engine/splashscreen.h new file mode 100644 index 00000000..8f8817c7 --- /dev/null +++ b/src/engine/splashscreen.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | |||
| 13 | // structs | ||
| 14 | |||
| 15 | |||
| 16 | // functions | ||
| 17 | |||
| 18 | void SplashScreenCreate( | ||
| 19 | __in HINSTANCE hInstance, | ||
| 20 | __in_z_opt LPCWSTR wzCaption, | ||
| 21 | __out HWND* pHwnd | ||
| 22 | ); | ||
| 23 | HRESULT SplashScreenDisplayError( | ||
| 24 | __in BOOTSTRAPPER_DISPLAY display, | ||
| 25 | __in_z LPCWSTR wzBundleName, | ||
| 26 | __in HRESULT hrError | ||
| 27 | ); | ||
| 28 | |||
| 29 | #if defined(__cplusplus) | ||
| 30 | } | ||
| 31 | #endif | ||
diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp new file mode 100644 index 00000000..cf111745 --- /dev/null +++ b/src/engine/uithread.cpp | |||
| @@ -0,0 +1,220 @@ | |||
| 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 | |||
| 5 | #define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow" | ||
| 6 | |||
| 7 | |||
| 8 | // structs | ||
| 9 | |||
| 10 | struct UITHREAD_CONTEXT | ||
| 11 | { | ||
| 12 | HANDLE hInitializedEvent; | ||
| 13 | HINSTANCE hInstance; | ||
| 14 | BURN_ENGINE_STATE* pEngineState; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct UITHREAD_INFO | ||
| 18 | { | ||
| 19 | BOOL fElevated; | ||
| 20 | BURN_USER_EXPERIENCE* pUserExperience; | ||
| 21 | }; | ||
| 22 | |||
| 23 | |||
| 24 | // internal function declarations | ||
| 25 | |||
| 26 | static DWORD WINAPI ThreadProc( | ||
| 27 | __in LPVOID pvContext | ||
| 28 | ); | ||
| 29 | |||
| 30 | static LRESULT CALLBACK WndProc( | ||
| 31 | __in HWND hWnd, | ||
| 32 | __in UINT uMsg, | ||
| 33 | __in WPARAM wParam, | ||
| 34 | __in LPARAM lParam | ||
| 35 | ); | ||
| 36 | |||
| 37 | |||
| 38 | // function definitions | ||
| 39 | |||
| 40 | HRESULT UiCreateMessageWindow( | ||
| 41 | __in HINSTANCE hInstance, | ||
| 42 | __in BURN_ENGINE_STATE* pEngineState | ||
| 43 | ) | ||
| 44 | { | ||
| 45 | HRESULT hr = S_OK; | ||
| 46 | HANDLE rgWaitHandles[2] = { }; | ||
| 47 | UITHREAD_CONTEXT context = { }; | ||
| 48 | |||
| 49 | // Create event to signal after the UI thread / window is initialized. | ||
| 50 | rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); | ||
| 51 | ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); | ||
| 52 | |||
| 53 | // Pass necessary information to create the window. | ||
| 54 | context.hInitializedEvent = rgWaitHandles[0]; | ||
| 55 | context.hInstance = hInstance; | ||
| 56 | context.pEngineState = pEngineState; | ||
| 57 | |||
| 58 | // Create our separate UI thread. | ||
| 59 | rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); | ||
| 60 | ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); | ||
| 61 | |||
| 62 | // Wait for either the thread to be initialized or the window to exit / fail prematurely. | ||
| 63 | ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); | ||
| 64 | |||
| 65 | pEngineState->hMessageWindowThread = rgWaitHandles[1]; | ||
| 66 | rgWaitHandles[1] = NULL; | ||
| 67 | |||
| 68 | LExit: | ||
| 69 | ReleaseHandle(rgWaitHandles[1]); | ||
| 70 | ReleaseHandle(rgWaitHandles[0]); | ||
| 71 | |||
| 72 | return hr; | ||
| 73 | } | ||
| 74 | |||
| 75 | void UiCloseMessageWindow( | ||
| 76 | __in BURN_ENGINE_STATE* pEngineState | ||
| 77 | ) | ||
| 78 | { | ||
| 79 | if (::IsWindow(pEngineState->hMessageWindow)) | ||
| 80 | { | ||
| 81 | ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0); | ||
| 82 | |||
| 83 | // Give the window 15 seconds to close because if it stays open it can prevent | ||
| 84 | // the engine from starting a reboot (should a reboot actually be necessary). | ||
| 85 | ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | // internal function definitions | ||
| 91 | |||
| 92 | static DWORD WINAPI ThreadProc( | ||
| 93 | __in LPVOID pvContext | ||
| 94 | ) | ||
| 95 | { | ||
| 96 | HRESULT hr = S_OK; | ||
| 97 | UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext); | ||
| 98 | UITHREAD_INFO info = { }; | ||
| 99 | |||
| 100 | WNDCLASSW wc = { }; | ||
| 101 | BOOL fRegistered = TRUE; | ||
| 102 | HWND hWnd = NULL; | ||
| 103 | |||
| 104 | BOOL fRet = FALSE; | ||
| 105 | MSG msg = { }; | ||
| 106 | |||
| 107 | BURN_ENGINE_STATE* pEngineState = pContext->pEngineState; | ||
| 108 | BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode; | ||
| 109 | |||
| 110 | // If elevated, set up the thread local storage to store the correct pipe to communicate logging. | ||
| 111 | if (fElevated) | ||
| 112 | { | ||
| 113 | Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId); | ||
| 114 | |||
| 115 | if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) | ||
| 116 | { | ||
| 117 | // If the function failed we cannot write to the pipe so just terminate. | ||
| 118 | ExitFunction1(hr = E_INVALIDSTATE); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | wc.lpfnWndProc = WndProc; | ||
| 123 | wc.hInstance = pContext->hInstance; | ||
| 124 | wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW; | ||
| 125 | |||
| 126 | if (!::RegisterClassW(&wc)) | ||
| 127 | { | ||
| 128 | ExitWithLastError(hr, "Failed to register window."); | ||
| 129 | } | ||
| 130 | |||
| 131 | fRegistered = TRUE; | ||
| 132 | |||
| 133 | info.fElevated = fElevated; | ||
| 134 | info.pUserExperience = &pEngineState->userExperience; | ||
| 135 | |||
| 136 | // Create the window to handle reboots without activating it. | ||
| 137 | hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info); | ||
| 138 | ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); | ||
| 139 | |||
| 140 | // Persist the window handle and let the caller know we've initialized. | ||
| 141 | pEngineState->hMessageWindow = hWnd; | ||
| 142 | ::SetEvent(pContext->hInitializedEvent); | ||
| 143 | |||
| 144 | // Pump messages until the window is closed. | ||
| 145 | while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) | ||
| 146 | { | ||
| 147 | if (-1 == fRet) | ||
| 148 | { | ||
| 149 | hr = E_UNEXPECTED; | ||
| 150 | ExitOnFailure(hr, "Unexpected return value from message pump."); | ||
| 151 | } | ||
| 152 | else if (!::IsDialogMessageW(msg.hwnd, &msg)) | ||
| 153 | { | ||
| 154 | ::TranslateMessage(&msg); | ||
| 155 | ::DispatchMessageW(&msg); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | LExit: | ||
| 160 | if (fRegistered) | ||
| 161 | { | ||
| 162 | ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); | ||
| 163 | } | ||
| 164 | |||
| 165 | return hr; | ||
| 166 | } | ||
| 167 | |||
| 168 | static LRESULT CALLBACK WndProc( | ||
| 169 | __in HWND hWnd, | ||
| 170 | __in UINT uMsg, | ||
| 171 | __in WPARAM wParam, | ||
| 172 | __in LPARAM lParam | ||
| 173 | ) | ||
| 174 | { | ||
| 175 | switch (uMsg) | ||
| 176 | { | ||
| 177 | case WM_NCCREATE: | ||
| 178 | { | ||
| 179 | LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam); | ||
| 180 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams)); | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | |||
| 184 | case WM_NCDESTROY: | ||
| 185 | { | ||
| 186 | LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 187 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); | ||
| 188 | return lRes; | ||
| 189 | } | ||
| 190 | |||
| 191 | case WM_QUERYENDSESSION: | ||
| 192 | { | ||
| 193 | DWORD dwEndSession = static_cast<DWORD>(lParam); | ||
| 194 | BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; | ||
| 195 | BOOL fCancel = TRUE; | ||
| 196 | BOOL fRet = FALSE; | ||
| 197 | |||
| 198 | // Always block shutdown in the elevated process, but ask the BA in the non-elevated. | ||
| 199 | UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA)); | ||
| 200 | if (!pInfo->fElevated) | ||
| 201 | { | ||
| 202 | // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel | ||
| 203 | // when the engine is doing work? | ||
| 204 | fCancel = !fCritical; | ||
| 205 | // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded. | ||
| 206 | UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel); | ||
| 207 | } | ||
| 208 | |||
| 209 | fRet = !fCancel; | ||
| 210 | LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet)); | ||
| 211 | return fRet; | ||
| 212 | } | ||
| 213 | |||
| 214 | case WM_DESTROY: | ||
| 215 | ::PostQuitMessage(0); | ||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); | ||
| 220 | } | ||
diff --git a/src/engine/uithread.h b/src/engine/uithread.h new file mode 100644 index 00000000..41168d52 --- /dev/null +++ b/src/engine/uithread.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // functions | ||
| 11 | |||
| 12 | HRESULT UiCreateMessageWindow( | ||
| 13 | __in HINSTANCE hInstance, | ||
| 14 | __in BURN_ENGINE_STATE* pEngineState | ||
| 15 | ); | ||
| 16 | |||
| 17 | void UiCloseMessageWindow( | ||
| 18 | __in BURN_ENGINE_STATE* pEngineState | ||
| 19 | ); | ||
| 20 | |||
| 21 | #if defined(__cplusplus) | ||
| 22 | } | ||
| 23 | #endif | ||
diff --git a/src/engine/update.cpp b/src/engine/update.cpp new file mode 100644 index 00000000..b04fa9a4 --- /dev/null +++ b/src/engine/update.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // internal function declarations | ||
| 7 | |||
| 8 | |||
| 9 | // function definitions | ||
| 10 | |||
| 11 | extern "C" HRESULT UpdateParseFromXml( | ||
| 12 | __in BURN_UPDATE* pUpdate, | ||
| 13 | __in IXMLDOMNode* pixnBundle | ||
| 14 | ) | ||
| 15 | { | ||
| 16 | HRESULT hr = S_OK; | ||
| 17 | IXMLDOMNode* pixnUpdateNode = NULL; | ||
| 18 | |||
| 19 | hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode); | ||
| 20 | if (S_FALSE == hr) | ||
| 21 | { | ||
| 22 | ExitFunction1(hr = S_OK); | ||
| 23 | } | ||
| 24 | ExitOnFailure(hr, "Failed to select Bundle/Update node."); | ||
| 25 | |||
| 26 | // @Location | ||
| 27 | hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource); | ||
| 28 | ExitOnFailure(hr, "Failed to get Update@Location."); | ||
| 29 | |||
| 30 | LExit: | ||
| 31 | ReleaseObject(pixnUpdateNode); | ||
| 32 | |||
| 33 | return hr; | ||
| 34 | } | ||
| 35 | |||
| 36 | extern "C" void UpdateUninitialize( | ||
| 37 | __in BURN_UPDATE* pUpdate | ||
| 38 | ) | ||
| 39 | { | ||
| 40 | PackageUninitialize(&pUpdate->package); | ||
| 41 | |||
| 42 | ReleaseStr(pUpdate->sczUpdateSource); | ||
| 43 | memset(pUpdate, 0, sizeof(BURN_UPDATE)); | ||
| 44 | } | ||
diff --git a/src/engine/update.h b/src/engine/update.h new file mode 100644 index 00000000..67d40481 --- /dev/null +++ b/src/engine/update.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // structs | ||
| 11 | |||
| 12 | typedef struct _BURN_UPDATE | ||
| 13 | { | ||
| 14 | BOOL fUpdateAvailable; | ||
| 15 | LPWSTR sczUpdateSource; | ||
| 16 | |||
| 17 | BURN_PACKAGE package; | ||
| 18 | } BURN_UPDATE; | ||
| 19 | |||
| 20 | |||
| 21 | // function declarations | ||
| 22 | |||
| 23 | HRESULT UpdateParseFromXml( | ||
| 24 | __in BURN_UPDATE* pUpdate, | ||
| 25 | __in IXMLDOMNode* pixnBundle | ||
| 26 | ); | ||
| 27 | void UpdateUninitialize( | ||
| 28 | __in BURN_UPDATE* pUpdate | ||
| 29 | ); | ||
| 30 | |||
| 31 | #if defined(__cplusplus) | ||
| 32 | } | ||
| 33 | #endif | ||
diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp new file mode 100644 index 00000000..8d5271aa --- /dev/null +++ b/src/engine/userexperience.cpp | |||
| @@ -0,0 +1,2122 @@ | |||
| 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 | |||
| 5 | // internal function declarations | ||
| 6 | |||
| 7 | static int FilterResult( | ||
| 8 | __in DWORD dwAllowedResults, | ||
| 9 | __in int nResult | ||
| 10 | ); | ||
| 11 | |||
| 12 | static HRESULT FilterExecuteResult( | ||
| 13 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 14 | __in HRESULT hrStatus, | ||
| 15 | __in BOOL fRollback, | ||
| 16 | __in BOOL fCancel, | ||
| 17 | __in LPCWSTR sczEventName | ||
| 18 | ); | ||
| 19 | |||
| 20 | |||
| 21 | // function definitions | ||
| 22 | |||
| 23 | /******************************************************************* | ||
| 24 | UserExperienceParseFromXml - | ||
| 25 | |||
| 26 | *******************************************************************/ | ||
| 27 | extern "C" HRESULT UserExperienceParseFromXml( | ||
| 28 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 29 | __in IXMLDOMNode* pixnBundle | ||
| 30 | ) | ||
| 31 | { | ||
| 32 | HRESULT hr = S_OK; | ||
| 33 | IXMLDOMNode* pixnUserExperienceNode = NULL; | ||
| 34 | |||
| 35 | // select UX node | ||
| 36 | hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode); | ||
| 37 | if (S_FALSE == hr) | ||
| 38 | { | ||
| 39 | hr = E_NOTFOUND; | ||
| 40 | } | ||
| 41 | ExitOnFailure(hr, "Failed to select user experience node."); | ||
| 42 | |||
| 43 | // parse splash screen | ||
| 44 | hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen); | ||
| 45 | if (E_NOTFOUND != hr) | ||
| 46 | { | ||
| 47 | ExitOnFailure(hr, "Failed to to get UX/@SplashScreen"); | ||
| 48 | } | ||
| 49 | |||
| 50 | // parse payloads | ||
| 51 | hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode); | ||
| 52 | ExitOnFailure(hr, "Failed to parse user experience payloads."); | ||
| 53 | |||
| 54 | // make sure we have at least one payload | ||
| 55 | if (0 == pUserExperience->payloads.cPayloads) | ||
| 56 | { | ||
| 57 | hr = E_UNEXPECTED; | ||
| 58 | ExitOnFailure(hr, "Too few UX payloads."); | ||
| 59 | } | ||
| 60 | |||
| 61 | LExit: | ||
| 62 | ReleaseObject(pixnUserExperienceNode); | ||
| 63 | |||
| 64 | return hr; | ||
| 65 | } | ||
| 66 | |||
| 67 | /******************************************************************* | ||
| 68 | UserExperienceUninitialize - | ||
| 69 | |||
| 70 | *******************************************************************/ | ||
| 71 | extern "C" void UserExperienceUninitialize( | ||
| 72 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 73 | ) | ||
| 74 | { | ||
| 75 | ReleaseStr(pUserExperience->sczTempDirectory); | ||
| 76 | PayloadsUninitialize(&pUserExperience->payloads); | ||
| 77 | |||
| 78 | // clear struct | ||
| 79 | memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE)); | ||
| 80 | } | ||
| 81 | |||
| 82 | /******************************************************************* | ||
| 83 | UserExperienceLoad - | ||
| 84 | |||
| 85 | *******************************************************************/ | ||
| 86 | extern "C" HRESULT UserExperienceLoad( | ||
| 87 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 88 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, | ||
| 89 | __in BOOTSTRAPPER_COMMAND* pCommand | ||
| 90 | ) | ||
| 91 | { | ||
| 92 | HRESULT hr = S_OK; | ||
| 93 | BOOTSTRAPPER_CREATE_ARGS args = { }; | ||
| 94 | BOOTSTRAPPER_CREATE_RESULTS results = { }; | ||
| 95 | |||
| 96 | args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS); | ||
| 97 | args.pCommand = pCommand; | ||
| 98 | args.pfnBootstrapperEngineProc = EngineForApplicationProc; | ||
| 99 | args.pvBootstrapperEngineProcContext = pEngineContext; | ||
| 100 | args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 5); // TODO: need to decide whether to keep this, and if so when to update it. | ||
| 101 | |||
| 102 | results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); | ||
| 103 | |||
| 104 | // Load BA DLL. | ||
| 105 | pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); | ||
| 106 | ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load UX DLL."); | ||
| 107 | |||
| 108 | // Get BootstrapperApplicationCreate entry-point. | ||
| 109 | PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); | ||
| 110 | ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point"); | ||
| 111 | |||
| 112 | // Create BA. | ||
| 113 | hr = pfnCreate(&args, &results); | ||
| 114 | ExitOnFailure(hr, "Failed to create BA."); | ||
| 115 | |||
| 116 | pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc; | ||
| 117 | pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext; | ||
| 118 | |||
| 119 | LExit: | ||
| 120 | return hr; | ||
| 121 | } | ||
| 122 | |||
| 123 | /******************************************************************* | ||
| 124 | UserExperienceUnload - | ||
| 125 | |||
| 126 | *******************************************************************/ | ||
| 127 | extern "C" HRESULT UserExperienceUnload( | ||
| 128 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 129 | ) | ||
| 130 | { | ||
| 131 | HRESULT hr = S_OK; | ||
| 132 | |||
| 133 | if (pUserExperience->hUXModule) | ||
| 134 | { | ||
| 135 | // Get BootstrapperApplicationDestroy entry-point and call it if it exists. | ||
| 136 | PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy"); | ||
| 137 | if (pfnDestroy) | ||
| 138 | { | ||
| 139 | pfnDestroy(); | ||
| 140 | } | ||
| 141 | |||
| 142 | // Free BA DLL. | ||
| 143 | if (!::FreeLibrary(pUserExperience->hUXModule)) | ||
| 144 | { | ||
| 145 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
| 146 | TraceError(hr, "Failed to unload BA DLL."); | ||
| 147 | } | ||
| 148 | pUserExperience->hUXModule = NULL; | ||
| 149 | } | ||
| 150 | |||
| 151 | //LExit: | ||
| 152 | return hr; | ||
| 153 | } | ||
| 154 | |||
| 155 | extern "C" HRESULT UserExperienceEnsureWorkingFolder( | ||
| 156 | __in LPCWSTR wzBundleId, | ||
| 157 | __deref_out_z LPWSTR* psczUserExperienceWorkingFolder | ||
| 158 | ) | ||
| 159 | { | ||
| 160 | HRESULT hr = S_OK; | ||
| 161 | LPWSTR sczWorkingFolder = NULL; | ||
| 162 | |||
| 163 | hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder); | ||
| 164 | ExitOnFailure(hr, "Failed to create working folder."); | ||
| 165 | |||
| 166 | hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba"); | ||
| 167 | ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path."); | ||
| 168 | |||
| 169 | hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL); | ||
| 170 | ExitOnFailure(hr, "Failed create bootstrapper application working folder."); | ||
| 171 | |||
| 172 | LExit: | ||
| 173 | ReleaseStr(sczWorkingFolder); | ||
| 174 | |||
| 175 | return hr; | ||
| 176 | } | ||
| 177 | |||
| 178 | |||
| 179 | extern "C" HRESULT UserExperienceRemove( | ||
| 180 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 181 | ) | ||
| 182 | { | ||
| 183 | HRESULT hr = S_OK; | ||
| 184 | |||
| 185 | // Remove temporary UX directory | ||
| 186 | if (pUserExperience->sczTempDirectory) | ||
| 187 | { | ||
| 188 | hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE); | ||
| 189 | TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder."); | ||
| 190 | } | ||
| 191 | |||
| 192 | //LExit: | ||
| 193 | return hr; | ||
| 194 | } | ||
| 195 | |||
| 196 | extern "C" int UserExperienceSendError( | ||
| 197 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 198 | __in BOOTSTRAPPER_ERROR_TYPE errorType, | ||
| 199 | __in_z_opt LPCWSTR wzPackageId, | ||
| 200 | __in HRESULT hrCode, | ||
| 201 | __in_z_opt LPCWSTR wzError, | ||
| 202 | __in DWORD uiFlags, | ||
| 203 | __in int nRecommendation | ||
| 204 | ) | ||
| 205 | { | ||
| 206 | int nResult = nRecommendation; | ||
| 207 | DWORD dwCode = HRESULT_CODE(hrCode); | ||
| 208 | LPWSTR sczError = NULL; | ||
| 209 | |||
| 210 | // If no error string was provided, try to get the error string from the HRESULT. | ||
| 211 | if (!wzError) | ||
| 212 | { | ||
| 213 | if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL))) | ||
| 214 | { | ||
| 215 | wzError = sczError; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value. | ||
| 220 | |||
| 221 | ReleaseStr(sczError); | ||
| 222 | return nResult; | ||
| 223 | } | ||
| 224 | |||
| 225 | extern "C" HRESULT UserExperienceActivateEngine( | ||
| 226 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 227 | __out_opt BOOL* pfActivated | ||
| 228 | ) | ||
| 229 | { | ||
| 230 | HRESULT hr = S_OK; | ||
| 231 | BOOL fActivated; | ||
| 232 | |||
| 233 | ::EnterCriticalSection(&pUserExperience->csEngineActive); | ||
| 234 | if (InterlockedCompareExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), TRUE, FALSE)) | ||
| 235 | { | ||
| 236 | AssertSz(FALSE, "Engine should have been deactivated before activating it."); | ||
| 237 | |||
| 238 | fActivated = FALSE; | ||
| 239 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE); | ||
| 240 | } | ||
| 241 | else | ||
| 242 | { | ||
| 243 | fActivated = TRUE; | ||
| 244 | } | ||
| 245 | ::LeaveCriticalSection(&pUserExperience->csEngineActive); | ||
| 246 | |||
| 247 | if (pfActivated) | ||
| 248 | { | ||
| 249 | *pfActivated = fActivated; | ||
| 250 | } | ||
| 251 | ExitOnRootFailure(hr, "Engine active cannot be changed because it was already in that state."); | ||
| 252 | |||
| 253 | LExit: | ||
| 254 | return hr; | ||
| 255 | } | ||
| 256 | |||
| 257 | extern "C" void UserExperienceDeactivateEngine( | ||
| 258 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 259 | ) | ||
| 260 | { | ||
| 261 | BOOL fActive = InterlockedExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), FALSE); | ||
| 262 | fActive = fActive; // prevents warning in "ship" build. | ||
| 263 | AssertSz(fActive, "Engine should have be active before deactivating it."); | ||
| 264 | } | ||
| 265 | |||
| 266 | extern "C" HRESULT UserExperienceEnsureEngineInactive( | ||
| 267 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 268 | ) | ||
| 269 | { | ||
| 270 | HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK; | ||
| 271 | ExitOnRootFailure(hr, "Engine is active, cannot proceed."); | ||
| 272 | |||
| 273 | LExit: | ||
| 274 | return hr; | ||
| 275 | } | ||
| 276 | |||
| 277 | extern "C" void UserExperienceExecuteReset( | ||
| 278 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 279 | ) | ||
| 280 | { | ||
| 281 | pUserExperience->hrApplyError = S_OK; | ||
| 282 | pUserExperience->hwndApply = NULL; | ||
| 283 | } | ||
| 284 | |||
| 285 | extern "C" void UserExperienceExecutePhaseComplete( | ||
| 286 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 287 | __in HRESULT hrResult | ||
| 288 | ) | ||
| 289 | { | ||
| 290 | if (FAILED(hrResult)) | ||
| 291 | { | ||
| 292 | pUserExperience->hrApplyError = hrResult; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | EXTERN_C BAAPI UserExperienceOnApplyBegin( | ||
| 297 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 298 | __in DWORD dwPhaseCount | ||
| 299 | ) | ||
| 300 | { | ||
| 301 | HRESULT hr = S_OK; | ||
| 302 | BA_ONAPPLYBEGIN_ARGS args = { }; | ||
| 303 | BA_ONAPPLYBEGIN_RESULTS results = { }; | ||
| 304 | |||
| 305 | args.cbSize = sizeof(args); | ||
| 306 | args.dwPhaseCount = dwPhaseCount; | ||
| 307 | |||
| 308 | results.cbSize = sizeof(results); | ||
| 309 | |||
| 310 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 311 | ExitOnFailure(hr, "BA OnApplyBegin failed."); | ||
| 312 | |||
| 313 | if (results.fCancel) | ||
| 314 | { | ||
| 315 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 316 | } | ||
| 317 | |||
| 318 | LExit: | ||
| 319 | return hr; | ||
| 320 | } | ||
| 321 | |||
| 322 | EXTERN_C BAAPI UserExperienceOnApplyComplete( | ||
| 323 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 324 | __in HRESULT hrStatus, | ||
| 325 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 326 | __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction | ||
| 327 | ) | ||
| 328 | { | ||
| 329 | HRESULT hr = S_OK; | ||
| 330 | BA_ONAPPLYCOMPLETE_ARGS args = { }; | ||
| 331 | BA_ONAPPLYCOMPLETE_RESULTS results = { }; | ||
| 332 | |||
| 333 | args.cbSize = sizeof(args); | ||
| 334 | args.hrStatus = hrStatus; | ||
| 335 | args.restart = restart; | ||
| 336 | args.recommendation = *pAction; | ||
| 337 | |||
| 338 | results.cbSize = sizeof(results); | ||
| 339 | results.action = *pAction; | ||
| 340 | |||
| 341 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 342 | ExitOnFailure(hr, "BA OnApplyComplete failed."); | ||
| 343 | |||
| 344 | *pAction = results.action; | ||
| 345 | |||
| 346 | LExit: | ||
| 347 | return hr; | ||
| 348 | } | ||
| 349 | |||
| 350 | EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin( | ||
| 351 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 352 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 353 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 354 | __in BOOTSTRAPPER_CACHE_OPERATION operation, | ||
| 355 | __in_z LPCWSTR wzSource | ||
| 356 | ) | ||
| 357 | { | ||
| 358 | HRESULT hr = S_OK; | ||
| 359 | BA_ONCACHEACQUIREBEGIN_ARGS args = { }; | ||
| 360 | BA_ONCACHEACQUIREBEGIN_RESULTS results = { }; | ||
| 361 | |||
| 362 | args.cbSize = sizeof(args); | ||
| 363 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 364 | args.wzPayloadId = wzPayloadId; | ||
| 365 | args.operation = operation; | ||
| 366 | args.wzSource = wzSource; | ||
| 367 | |||
| 368 | results.cbSize = sizeof(results); | ||
| 369 | |||
| 370 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 371 | ExitOnFailure(hr, "BA OnCacheAcquireBegin failed."); | ||
| 372 | |||
| 373 | if (results.fCancel) | ||
| 374 | { | ||
| 375 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 376 | } | ||
| 377 | |||
| 378 | LExit: | ||
| 379 | return hr; | ||
| 380 | } | ||
| 381 | |||
| 382 | EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete( | ||
| 383 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 384 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 385 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 386 | __in HRESULT hrStatus, | ||
| 387 | __inout BOOL* pfRetry | ||
| 388 | ) | ||
| 389 | { | ||
| 390 | HRESULT hr = S_OK; | ||
| 391 | BA_ONCACHEACQUIRECOMPLETE_ARGS args = { }; | ||
| 392 | BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { }; | ||
| 393 | |||
| 394 | args.cbSize = sizeof(args); | ||
| 395 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 396 | args.wzPayloadId = wzPayloadId; | ||
| 397 | args.hrStatus = hrStatus; | ||
| 398 | args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE; | ||
| 399 | |||
| 400 | results.cbSize = sizeof(results); | ||
| 401 | results.action = args.recommendation; | ||
| 402 | |||
| 403 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 404 | ExitOnFailure(hr, "BA OnCacheAcquireComplete failed."); | ||
| 405 | |||
| 406 | if (FAILED(hrStatus)) | ||
| 407 | { | ||
| 408 | *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action; | ||
| 409 | } | ||
| 410 | |||
| 411 | LExit: | ||
| 412 | return hr; | ||
| 413 | } | ||
| 414 | |||
| 415 | EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress( | ||
| 416 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 417 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 418 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 419 | __in DWORD64 dw64Progress, | ||
| 420 | __in DWORD64 dw64Total, | ||
| 421 | __in DWORD dwOverallPercentage | ||
| 422 | ) | ||
| 423 | { | ||
| 424 | HRESULT hr = S_OK; | ||
| 425 | BA_ONCACHEACQUIREPROGRESS_ARGS args = { }; | ||
| 426 | BA_ONCACHEACQUIREPROGRESS_RESULTS results = { }; | ||
| 427 | |||
| 428 | args.cbSize = sizeof(args); | ||
| 429 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 430 | args.wzPayloadId = wzPayloadId; | ||
| 431 | args.dw64Progress = dw64Progress; | ||
| 432 | args.dw64Total = dw64Total; | ||
| 433 | args.dwOverallPercentage = dwOverallPercentage; | ||
| 434 | |||
| 435 | results.cbSize = sizeof(results); | ||
| 436 | |||
| 437 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results, pUserExperience->pvBAProcContext); | ||
| 438 | ExitOnFailure(hr, "BA OnCacheAcquireProgress failed."); | ||
| 439 | |||
| 440 | if (results.fCancel) | ||
| 441 | { | ||
| 442 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 443 | } | ||
| 444 | |||
| 445 | LExit: | ||
| 446 | return hr; | ||
| 447 | } | ||
| 448 | |||
| 449 | EXTERN_C BAAPI UserExperienceOnCacheBegin( | ||
| 450 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 451 | ) | ||
| 452 | { | ||
| 453 | HRESULT hr = S_OK; | ||
| 454 | BA_ONCACHEBEGIN_ARGS args = { }; | ||
| 455 | BA_ONCACHEBEGIN_RESULTS results = { }; | ||
| 456 | |||
| 457 | args.cbSize = sizeof(args); | ||
| 458 | |||
| 459 | results.cbSize = sizeof(results); | ||
| 460 | |||
| 461 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 462 | ExitOnFailure(hr, "BA OnCacheBegin failed."); | ||
| 463 | |||
| 464 | if (results.fCancel) | ||
| 465 | { | ||
| 466 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 467 | } | ||
| 468 | |||
| 469 | LExit: | ||
| 470 | return hr; | ||
| 471 | } | ||
| 472 | |||
| 473 | EXTERN_C BAAPI UserExperienceOnCacheComplete( | ||
| 474 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 475 | __in HRESULT hrStatus | ||
| 476 | ) | ||
| 477 | { | ||
| 478 | HRESULT hr = S_OK; | ||
| 479 | BA_ONCACHECOMPLETE_ARGS args = { }; | ||
| 480 | BA_ONCACHECOMPLETE_RESULTS results = { }; | ||
| 481 | |||
| 482 | args.cbSize = sizeof(args); | ||
| 483 | args.hrStatus = hrStatus; | ||
| 484 | |||
| 485 | results.cbSize = sizeof(results); | ||
| 486 | |||
| 487 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 488 | ExitOnFailure(hr, "BA OnCacheComplete failed."); | ||
| 489 | |||
| 490 | LExit: | ||
| 491 | return hr; | ||
| 492 | } | ||
| 493 | |||
| 494 | EXTERN_C BAAPI UserExperienceOnCachePackageBegin( | ||
| 495 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 496 | __in_z LPCWSTR wzPackageId, | ||
| 497 | __in DWORD cCachePayloads, | ||
| 498 | __in DWORD64 dw64PackageCacheSize | ||
| 499 | ) | ||
| 500 | { | ||
| 501 | HRESULT hr = S_OK; | ||
| 502 | BA_ONCACHEPACKAGEBEGIN_ARGS args = { }; | ||
| 503 | BA_ONCACHEPACKAGEBEGIN_RESULTS results = { }; | ||
| 504 | |||
| 505 | args.cbSize = sizeof(args); | ||
| 506 | args.wzPackageId = wzPackageId; | ||
| 507 | args.cCachePayloads = cCachePayloads; | ||
| 508 | args.dw64PackageCacheSize = dw64PackageCacheSize; | ||
| 509 | |||
| 510 | results.cbSize = sizeof(results); | ||
| 511 | |||
| 512 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 513 | ExitOnFailure(hr, "BA OnCachePackageBegin failed."); | ||
| 514 | |||
| 515 | if (results.fCancel) | ||
| 516 | { | ||
| 517 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 518 | } | ||
| 519 | |||
| 520 | LExit: | ||
| 521 | return hr; | ||
| 522 | } | ||
| 523 | |||
| 524 | EXTERN_C BAAPI UserExperienceOnCachePackageComplete( | ||
| 525 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 526 | __in_z LPCWSTR wzPackageId, | ||
| 527 | __in HRESULT hrStatus, | ||
| 528 | __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction | ||
| 529 | ) | ||
| 530 | { | ||
| 531 | HRESULT hr = S_OK; | ||
| 532 | BA_ONCACHEPACKAGECOMPLETE_ARGS args = { }; | ||
| 533 | BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { }; | ||
| 534 | |||
| 535 | args.cbSize = sizeof(args); | ||
| 536 | args.wzPackageId = wzPackageId; | ||
| 537 | args.hrStatus = hrStatus; | ||
| 538 | args.recommendation = *pAction; | ||
| 539 | |||
| 540 | results.cbSize = sizeof(results); | ||
| 541 | results.action = *pAction; | ||
| 542 | |||
| 543 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 544 | ExitOnFailure(hr, "BA OnCachePackageComplete failed."); | ||
| 545 | |||
| 546 | if (FAILED(hrStatus)) | ||
| 547 | { | ||
| 548 | *pAction = results.action; | ||
| 549 | } | ||
| 550 | |||
| 551 | LExit: | ||
| 552 | return hr; | ||
| 553 | } | ||
| 554 | |||
| 555 | EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin( | ||
| 556 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 557 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 558 | __in_z_opt LPCWSTR wzPayloadId | ||
| 559 | ) | ||
| 560 | { | ||
| 561 | HRESULT hr = S_OK; | ||
| 562 | BA_ONCACHEVERIFYBEGIN_ARGS args = { }; | ||
| 563 | BA_ONCACHEVERIFYBEGIN_RESULTS results = { }; | ||
| 564 | |||
| 565 | args.cbSize = sizeof(args); | ||
| 566 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 567 | args.wzPayloadId = wzPayloadId; | ||
| 568 | |||
| 569 | results.cbSize = sizeof(results); | ||
| 570 | |||
| 571 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 572 | ExitOnFailure(hr, "BA OnCacheVerifyBegin failed."); | ||
| 573 | |||
| 574 | if (results.fCancel) | ||
| 575 | { | ||
| 576 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 577 | } | ||
| 578 | |||
| 579 | LExit: | ||
| 580 | return hr; | ||
| 581 | } | ||
| 582 | |||
| 583 | EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete( | ||
| 584 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 585 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 586 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 587 | __in HRESULT hrStatus, | ||
| 588 | __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction | ||
| 589 | ) | ||
| 590 | { | ||
| 591 | HRESULT hr = S_OK; | ||
| 592 | BA_ONCACHEVERIFYCOMPLETE_ARGS args = { }; | ||
| 593 | BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { }; | ||
| 594 | |||
| 595 | args.cbSize = sizeof(args); | ||
| 596 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 597 | args.wzPayloadId = wzPayloadId; | ||
| 598 | args.hrStatus = hrStatus; | ||
| 599 | args.recommendation = *pAction; | ||
| 600 | |||
| 601 | results.cbSize = sizeof(results); | ||
| 602 | results.action = *pAction; | ||
| 603 | |||
| 604 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 605 | ExitOnFailure(hr, "BA OnCacheVerifyComplete failed."); | ||
| 606 | |||
| 607 | if (FAILED(hrStatus)) | ||
| 608 | { | ||
| 609 | *pAction = results.action; | ||
| 610 | } | ||
| 611 | |||
| 612 | LExit: | ||
| 613 | return hr; | ||
| 614 | } | ||
| 615 | |||
| 616 | EXTERN_C BAAPI UserExperienceOnDetectBegin( | ||
| 617 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 618 | __in BOOL fInstalled, | ||
| 619 | __in DWORD cPackages | ||
| 620 | ) | ||
| 621 | { | ||
| 622 | HRESULT hr = S_OK; | ||
| 623 | BA_ONDETECTBEGIN_ARGS args = { }; | ||
| 624 | BA_ONDETECTBEGIN_RESULTS results = { }; | ||
| 625 | |||
| 626 | args.cbSize = sizeof(args); | ||
| 627 | args.cPackages = cPackages; | ||
| 628 | args.fInstalled = fInstalled; | ||
| 629 | |||
| 630 | results.cbSize = sizeof(results); | ||
| 631 | |||
| 632 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 633 | ExitOnFailure(hr, "BA OnDetectBegin failed."); | ||
| 634 | |||
| 635 | if (results.fCancel) | ||
| 636 | { | ||
| 637 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 638 | } | ||
| 639 | |||
| 640 | LExit: | ||
| 641 | return hr; | ||
| 642 | } | ||
| 643 | |||
| 644 | EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage( | ||
| 645 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 646 | __in_z LPCWSTR wzPackageId, | ||
| 647 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 648 | __in DWORD64 dw64CompatiblePackageVersion | ||
| 649 | ) | ||
| 650 | { | ||
| 651 | HRESULT hr = S_OK; | ||
| 652 | BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { }; | ||
| 653 | BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { }; | ||
| 654 | |||
| 655 | args.cbSize = sizeof(args); | ||
| 656 | args.wzPackageId = wzPackageId; | ||
| 657 | args.wzCompatiblePackageId = wzCompatiblePackageId; | ||
| 658 | args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; | ||
| 659 | |||
| 660 | results.cbSize = sizeof(results); | ||
| 661 | |||
| 662 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 663 | ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed."); | ||
| 664 | |||
| 665 | if (results.fCancel) | ||
| 666 | { | ||
| 667 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 668 | } | ||
| 669 | |||
| 670 | LExit: | ||
| 671 | return hr; | ||
| 672 | } | ||
| 673 | |||
| 674 | EXTERN_C BAAPI UserExperienceOnDetectComplete( | ||
| 675 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 676 | __in HRESULT hrStatus | ||
| 677 | ) | ||
| 678 | { | ||
| 679 | HRESULT hr = S_OK; | ||
| 680 | BA_ONDETECTCOMPLETE_ARGS args = { }; | ||
| 681 | BA_ONDETECTCOMPLETE_RESULTS results = { }; | ||
| 682 | |||
| 683 | args.cbSize = sizeof(args); | ||
| 684 | args.hrStatus = hrStatus; | ||
| 685 | |||
| 686 | results.cbSize = sizeof(results); | ||
| 687 | |||
| 688 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 689 | ExitOnFailure(hr, "BA OnDetectComplete failed."); | ||
| 690 | |||
| 691 | LExit: | ||
| 692 | return hr; | ||
| 693 | } | ||
| 694 | |||
| 695 | EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle( | ||
| 696 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 697 | __in_z LPCWSTR wzBundleId, | ||
| 698 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 699 | __in_z LPCWSTR wzBundleTag, | ||
| 700 | __in BOOL fPerMachine, | ||
| 701 | __in DWORD64 dw64Version, | ||
| 702 | __inout BOOL* pfIgnoreBundle | ||
| 703 | ) | ||
| 704 | { | ||
| 705 | HRESULT hr = S_OK; | ||
| 706 | BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { }; | ||
| 707 | BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { }; | ||
| 708 | |||
| 709 | args.cbSize = sizeof(args); | ||
| 710 | args.wzBundleId = wzBundleId; | ||
| 711 | args.relationType = relationType; | ||
| 712 | args.wzBundleTag = wzBundleTag; | ||
| 713 | args.fPerMachine = fPerMachine; | ||
| 714 | args.dw64Version = dw64Version; | ||
| 715 | |||
| 716 | results.cbSize = sizeof(results); | ||
| 717 | results.fIgnoreBundle = *pfIgnoreBundle; | ||
| 718 | |||
| 719 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 720 | ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed."); | ||
| 721 | |||
| 722 | if (results.fCancel) | ||
| 723 | { | ||
| 724 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 725 | } | ||
| 726 | *pfIgnoreBundle = results.fIgnoreBundle; | ||
| 727 | |||
| 728 | LExit: | ||
| 729 | return hr; | ||
| 730 | } | ||
| 731 | |||
| 732 | EXTERN_C BAAPI UserExperienceOnDetectMsiFeature( | ||
| 733 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 734 | __in_z LPCWSTR wzPackageId, | ||
| 735 | __in_z LPCWSTR wzFeatureId, | ||
| 736 | __in BOOTSTRAPPER_FEATURE_STATE state | ||
| 737 | ) | ||
| 738 | { | ||
| 739 | HRESULT hr = S_OK; | ||
| 740 | BA_ONDETECTMSIFEATURE_ARGS args = { }; | ||
| 741 | BA_ONDETECTMSIFEATURE_RESULTS results = { }; | ||
| 742 | |||
| 743 | args.cbSize = sizeof(args); | ||
| 744 | args.wzPackageId = wzPackageId; | ||
| 745 | args.wzFeatureId = wzFeatureId; | ||
| 746 | args.state = state; | ||
| 747 | |||
| 748 | results.cbSize = sizeof(results); | ||
| 749 | |||
| 750 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 751 | ExitOnFailure(hr, "BA OnDetectMsiFeature failed."); | ||
| 752 | |||
| 753 | if (results.fCancel) | ||
| 754 | { | ||
| 755 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 756 | } | ||
| 757 | |||
| 758 | LExit: | ||
| 759 | return hr; | ||
| 760 | } | ||
| 761 | |||
| 762 | EXTERN_C BAAPI UserExperienceOnDetectPackageBegin( | ||
| 763 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 764 | __in_z LPCWSTR wzPackageId | ||
| 765 | ) | ||
| 766 | { | ||
| 767 | HRESULT hr = S_OK; | ||
| 768 | BA_ONDETECTPACKAGEBEGIN_ARGS args = { }; | ||
| 769 | BA_ONDETECTPACKAGEBEGIN_RESULTS results = { }; | ||
| 770 | |||
| 771 | args.cbSize = sizeof(args); | ||
| 772 | args.wzPackageId = wzPackageId; | ||
| 773 | |||
| 774 | results.cbSize = sizeof(results); | ||
| 775 | |||
| 776 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 777 | ExitOnFailure(hr, "BA OnDetectPackageBegin failed."); | ||
| 778 | |||
| 779 | if (results.fCancel) | ||
| 780 | { | ||
| 781 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 782 | } | ||
| 783 | |||
| 784 | LExit: | ||
| 785 | return hr; | ||
| 786 | } | ||
| 787 | |||
| 788 | EXTERN_C BAAPI UserExperienceOnDetectPackageComplete( | ||
| 789 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 790 | __in_z LPCWSTR wzPackageId, | ||
| 791 | __in HRESULT hrStatus, | ||
| 792 | __in BOOTSTRAPPER_PACKAGE_STATE state | ||
| 793 | ) | ||
| 794 | { | ||
| 795 | HRESULT hr = S_OK; | ||
| 796 | BA_ONDETECTPACKAGECOMPLETE_ARGS args = { }; | ||
| 797 | BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { }; | ||
| 798 | |||
| 799 | args.cbSize = sizeof(args); | ||
| 800 | args.wzPackageId = wzPackageId; | ||
| 801 | args.hrStatus = hrStatus; | ||
| 802 | args.state = state; | ||
| 803 | |||
| 804 | results.cbSize = sizeof(results); | ||
| 805 | |||
| 806 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 807 | ExitOnFailure(hr, "BA OnDetectPackageComplete failed."); | ||
| 808 | |||
| 809 | LExit: | ||
| 810 | return hr; | ||
| 811 | } | ||
| 812 | |||
| 813 | EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle( | ||
| 814 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 815 | __in_z LPCWSTR wzBundleId, | ||
| 816 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 817 | __in_z LPCWSTR wzBundleTag, | ||
| 818 | __in BOOL fPerMachine, | ||
| 819 | __in DWORD64 dw64Version, | ||
| 820 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 821 | ) | ||
| 822 | { | ||
| 823 | HRESULT hr = S_OK; | ||
| 824 | BA_ONDETECTRELATEDBUNDLE_ARGS args = { }; | ||
| 825 | BA_ONDETECTRELATEDBUNDLE_RESULTS results = { }; | ||
| 826 | |||
| 827 | args.cbSize = sizeof(args); | ||
| 828 | args.wzBundleId = wzBundleId; | ||
| 829 | args.relationType = relationType; | ||
| 830 | args.wzBundleTag = wzBundleTag; | ||
| 831 | args.fPerMachine = fPerMachine; | ||
| 832 | args.dw64Version = dw64Version; | ||
| 833 | args.operation = operation; | ||
| 834 | |||
| 835 | results.cbSize = sizeof(results); | ||
| 836 | |||
| 837 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 838 | ExitOnFailure(hr, "BA OnDetectRelatedBundle failed."); | ||
| 839 | |||
| 840 | if (results.fCancel) | ||
| 841 | { | ||
| 842 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 843 | } | ||
| 844 | |||
| 845 | LExit: | ||
| 846 | return hr; | ||
| 847 | } | ||
| 848 | |||
| 849 | EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( | ||
| 850 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 851 | __in_z LPCWSTR wzPackageId, | ||
| 852 | __in_z LPCWSTR wzUpgradeCode, | ||
| 853 | __in_z LPCWSTR wzProductCode, | ||
| 854 | __in BOOL fPerMachine, | ||
| 855 | __in DWORD64 dw64Version, | ||
| 856 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 857 | ) | ||
| 858 | { | ||
| 859 | HRESULT hr = S_OK; | ||
| 860 | BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { }; | ||
| 861 | BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { }; | ||
| 862 | |||
| 863 | args.cbSize = sizeof(args); | ||
| 864 | args.wzPackageId = wzPackageId; | ||
| 865 | args.wzUpgradeCode = wzUpgradeCode; | ||
| 866 | args.wzProductCode = wzProductCode; | ||
| 867 | args.fPerMachine = fPerMachine; | ||
| 868 | args.dw64Version = dw64Version; | ||
| 869 | args.operation = operation; | ||
| 870 | |||
| 871 | results.cbSize = sizeof(results); | ||
| 872 | |||
| 873 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 874 | ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed."); | ||
| 875 | |||
| 876 | if (results.fCancel) | ||
| 877 | { | ||
| 878 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 879 | } | ||
| 880 | |||
| 881 | LExit: | ||
| 882 | return hr; | ||
| 883 | } | ||
| 884 | |||
| 885 | EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage( | ||
| 886 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 887 | __in_z LPCWSTR wzPackageId, | ||
| 888 | __in_z LPCWSTR wzProductCode, | ||
| 889 | __in BOOTSTRAPPER_PACKAGE_STATE patchState | ||
| 890 | ) | ||
| 891 | { | ||
| 892 | HRESULT hr = S_OK; | ||
| 893 | BA_ONDETECTTARGETMSIPACKAGE_ARGS args = { }; | ||
| 894 | BA_ONDETECTTARGETMSIPACKAGE_RESULTS results = { }; | ||
| 895 | |||
| 896 | args.cbSize = sizeof(args); | ||
| 897 | args.wzPackageId = wzPackageId; | ||
| 898 | args.wzProductCode = wzProductCode; | ||
| 899 | args.patchState = patchState; | ||
| 900 | |||
| 901 | results.cbSize = sizeof(results); | ||
| 902 | |||
| 903 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 904 | ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed."); | ||
| 905 | |||
| 906 | if (results.fCancel) | ||
| 907 | { | ||
| 908 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 909 | } | ||
| 910 | |||
| 911 | LExit: | ||
| 912 | return hr; | ||
| 913 | } | ||
| 914 | |||
| 915 | EXTERN_C BAAPI UserExperienceOnDetectUpdate( | ||
| 916 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 917 | __in_z LPCWSTR wzUpdateLocation, | ||
| 918 | __in DWORD64 dw64Size, | ||
| 919 | __in DWORD64 dw64Version, | ||
| 920 | __in_z_opt LPCWSTR wzTitle, | ||
| 921 | __in_z_opt LPCWSTR wzSummary, | ||
| 922 | __in_z_opt LPCWSTR wzContentType, | ||
| 923 | __in_z_opt LPCWSTR wzContent, | ||
| 924 | __inout BOOL* pfStopProcessingUpdates | ||
| 925 | ) | ||
| 926 | { | ||
| 927 | HRESULT hr = S_OK; | ||
| 928 | BA_ONDETECTUPDATE_ARGS args = { }; | ||
| 929 | BA_ONDETECTUPDATE_RESULTS results = { }; | ||
| 930 | |||
| 931 | args.cbSize = sizeof(args); | ||
| 932 | args.wzUpdateLocation = wzUpdateLocation; | ||
| 933 | args.dw64Size = dw64Size; | ||
| 934 | args.dw64Version = dw64Version; | ||
| 935 | args.wzTitle = wzTitle; | ||
| 936 | args.wzSummary = wzSummary; | ||
| 937 | args.wzContentType = wzContentType; | ||
| 938 | args.wzContent = wzContent; | ||
| 939 | |||
| 940 | results.cbSize = sizeof(results); | ||
| 941 | results.fStopProcessingUpdates = *pfStopProcessingUpdates; | ||
| 942 | |||
| 943 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 944 | ExitOnFailure(hr, "BA OnDetectUpdate failed."); | ||
| 945 | |||
| 946 | if (results.fCancel) | ||
| 947 | { | ||
| 948 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 949 | } | ||
| 950 | *pfStopProcessingUpdates = results.fStopProcessingUpdates; | ||
| 951 | |||
| 952 | LExit: | ||
| 953 | return hr; | ||
| 954 | } | ||
| 955 | |||
| 956 | EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin( | ||
| 957 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 958 | __in_z LPCWSTR wzUpdateLocation, | ||
| 959 | __inout BOOL* pfSkip | ||
| 960 | ) | ||
| 961 | { | ||
| 962 | HRESULT hr = S_OK; | ||
| 963 | BA_ONDETECTUPDATEBEGIN_ARGS args = { }; | ||
| 964 | BA_ONDETECTUPDATEBEGIN_RESULTS results = { }; | ||
| 965 | |||
| 966 | args.cbSize = sizeof(args); | ||
| 967 | args.wzUpdateLocation = wzUpdateLocation; | ||
| 968 | |||
| 969 | results.cbSize = sizeof(results); | ||
| 970 | results.fSkip = *pfSkip; | ||
| 971 | |||
| 972 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 973 | ExitOnFailure(hr, "BA OnDetectUpdateBegin failed."); | ||
| 974 | |||
| 975 | if (results.fCancel) | ||
| 976 | { | ||
| 977 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 978 | } | ||
| 979 | *pfSkip = results.fSkip; | ||
| 980 | |||
| 981 | LExit: | ||
| 982 | return hr; | ||
| 983 | } | ||
| 984 | |||
| 985 | EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete( | ||
| 986 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 987 | __in HRESULT hrStatus, | ||
| 988 | __inout BOOL* pfIgnoreError | ||
| 989 | ) | ||
| 990 | { | ||
| 991 | HRESULT hr = S_OK; | ||
| 992 | BA_ONDETECTUPDATECOMPLETE_ARGS args = { }; | ||
| 993 | BA_ONDETECTUPDATECOMPLETE_RESULTS results = { }; | ||
| 994 | |||
| 995 | args.cbSize = sizeof(args); | ||
| 996 | args.hrStatus = hrStatus; | ||
| 997 | |||
| 998 | results.cbSize = sizeof(results); | ||
| 999 | results.fIgnoreError = *pfIgnoreError; | ||
| 1000 | |||
| 1001 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1002 | ExitOnFailure(hr, "BA OnDetectUpdateComplete failed."); | ||
| 1003 | |||
| 1004 | if (FAILED(hrStatus)) | ||
| 1005 | { | ||
| 1006 | *pfIgnoreError = results.fIgnoreError; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | LExit: | ||
| 1010 | return hr; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | EXTERN_C BAAPI UserExperienceOnElevateBegin( | ||
| 1014 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 1015 | ) | ||
| 1016 | { | ||
| 1017 | HRESULT hr = S_OK; | ||
| 1018 | BA_ONELEVATEBEGIN_ARGS args = { }; | ||
| 1019 | BA_ONELEVATEBEGIN_RESULTS results = { }; | ||
| 1020 | |||
| 1021 | args.cbSize = sizeof(args); | ||
| 1022 | |||
| 1023 | results.cbSize = sizeof(results); | ||
| 1024 | |||
| 1025 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1026 | ExitOnFailure(hr, "BA OnElevateBegin failed."); | ||
| 1027 | |||
| 1028 | if (results.fCancel) | ||
| 1029 | { | ||
| 1030 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | LExit: | ||
| 1034 | return hr; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | EXTERN_C BAAPI UserExperienceOnElevateComplete( | ||
| 1038 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1039 | __in HRESULT hrStatus | ||
| 1040 | ) | ||
| 1041 | { | ||
| 1042 | HRESULT hr = S_OK; | ||
| 1043 | BA_ONELEVATECOMPLETE_ARGS args = { }; | ||
| 1044 | BA_ONELEVATECOMPLETE_RESULTS results = { }; | ||
| 1045 | |||
| 1046 | args.cbSize = sizeof(args); | ||
| 1047 | args.hrStatus = hrStatus; | ||
| 1048 | |||
| 1049 | results.cbSize = sizeof(results); | ||
| 1050 | |||
| 1051 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1052 | ExitOnFailure(hr, "BA OnElevateComplete failed."); | ||
| 1053 | |||
| 1054 | LExit: | ||
| 1055 | return hr; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | EXTERN_C BAAPI UserExperienceOnError( | ||
| 1059 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1060 | __in BOOTSTRAPPER_ERROR_TYPE errorType, | ||
| 1061 | __in_z_opt LPCWSTR wzPackageId, | ||
| 1062 | __in DWORD dwCode, | ||
| 1063 | __in_z_opt LPCWSTR wzError, | ||
| 1064 | __in DWORD dwUIHint, | ||
| 1065 | __in DWORD cData, | ||
| 1066 | __in_ecount_z_opt(cData) LPCWSTR* rgwzData, | ||
| 1067 | __inout int* pnResult | ||
| 1068 | ) | ||
| 1069 | { | ||
| 1070 | HRESULT hr = S_OK; | ||
| 1071 | BA_ONERROR_ARGS args = { }; | ||
| 1072 | BA_ONERROR_RESULTS results = { }; | ||
| 1073 | |||
| 1074 | args.cbSize = sizeof(args); | ||
| 1075 | args.errorType = errorType; | ||
| 1076 | args.wzPackageId = wzPackageId; | ||
| 1077 | args.dwCode = dwCode; | ||
| 1078 | args.wzError = wzError; | ||
| 1079 | args.dwUIHint = dwUIHint; | ||
| 1080 | args.cData = cData; | ||
| 1081 | args.rgwzData = rgwzData; | ||
| 1082 | args.nRecommendation = *pnResult; | ||
| 1083 | |||
| 1084 | results.cbSize = sizeof(results); | ||
| 1085 | results.nResult = *pnResult; | ||
| 1086 | |||
| 1087 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1088 | ExitOnFailure(hr, "BA OnError failed."); | ||
| 1089 | |||
| 1090 | *pnResult = results.nResult; | ||
| 1091 | |||
| 1092 | LExit: | ||
| 1093 | return hr; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | EXTERN_C BAAPI UserExperienceOnExecuteBegin( | ||
| 1097 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1098 | __in DWORD cExecutingPackages | ||
| 1099 | ) | ||
| 1100 | { | ||
| 1101 | HRESULT hr = S_OK; | ||
| 1102 | BA_ONEXECUTEBEGIN_ARGS args = { }; | ||
| 1103 | BA_ONEXECUTEBEGIN_RESULTS results = { }; | ||
| 1104 | |||
| 1105 | args.cbSize = sizeof(args); | ||
| 1106 | args.cExecutingPackages = cExecutingPackages; | ||
| 1107 | |||
| 1108 | results.cbSize = sizeof(results); | ||
| 1109 | |||
| 1110 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1111 | ExitOnFailure(hr, "BA OnExecuteBegin failed."); | ||
| 1112 | |||
| 1113 | if (results.fCancel) | ||
| 1114 | { | ||
| 1115 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | LExit: | ||
| 1119 | return hr; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | EXTERN_C BAAPI UserExperienceOnExecuteComplete( | ||
| 1123 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1124 | __in HRESULT hrStatus | ||
| 1125 | ) | ||
| 1126 | { | ||
| 1127 | HRESULT hr = S_OK; | ||
| 1128 | BA_ONEXECUTECOMPLETE_ARGS args = { }; | ||
| 1129 | BA_ONEXECUTECOMPLETE_RESULTS results = { }; | ||
| 1130 | |||
| 1131 | args.cbSize = sizeof(args); | ||
| 1132 | args.hrStatus = hrStatus; | ||
| 1133 | |||
| 1134 | results.cbSize = sizeof(results); | ||
| 1135 | |||
| 1136 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1137 | ExitOnFailure(hr, "BA OnExecuteComplete failed."); | ||
| 1138 | |||
| 1139 | LExit: | ||
| 1140 | return hr; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse( | ||
| 1144 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1145 | __in_z LPCWSTR wzPackageId, | ||
| 1146 | __in DWORD cFiles, | ||
| 1147 | __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, | ||
| 1148 | __inout int* pnResult | ||
| 1149 | ) | ||
| 1150 | { | ||
| 1151 | HRESULT hr = S_OK; | ||
| 1152 | BA_ONEXECUTEFILESINUSE_ARGS args = { }; | ||
| 1153 | BA_ONEXECUTEFILESINUSE_RESULTS results = { }; | ||
| 1154 | |||
| 1155 | args.cbSize = sizeof(args); | ||
| 1156 | args.wzPackageId = wzPackageId; | ||
| 1157 | args.cFiles = cFiles; | ||
| 1158 | args.rgwzFiles = rgwzFiles; | ||
| 1159 | args.nRecommendation = *pnResult; | ||
| 1160 | |||
| 1161 | results.cbSize = sizeof(results); | ||
| 1162 | results.nResult = *pnResult; | ||
| 1163 | |||
| 1164 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1165 | ExitOnFailure(hr, "BA OnExecuteFilesInUse failed."); | ||
| 1166 | |||
| 1167 | *pnResult = results.nResult; | ||
| 1168 | |||
| 1169 | LExit: | ||
| 1170 | return hr; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage( | ||
| 1174 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1175 | __in_z LPCWSTR wzPackageId, | ||
| 1176 | __in INSTALLMESSAGE messageType, | ||
| 1177 | __in DWORD dwUIHint, | ||
| 1178 | __in_z LPCWSTR wzMessage, | ||
| 1179 | __in DWORD cData, | ||
| 1180 | __in_ecount_z_opt(cData) LPCWSTR* rgwzData, | ||
| 1181 | __inout int* pnResult | ||
| 1182 | ) | ||
| 1183 | { | ||
| 1184 | HRESULT hr = S_OK; | ||
| 1185 | BA_ONEXECUTEMSIMESSAGE_ARGS args = { }; | ||
| 1186 | BA_ONEXECUTEMSIMESSAGE_RESULTS results = { }; | ||
| 1187 | |||
| 1188 | args.cbSize = sizeof(args); | ||
| 1189 | args.wzPackageId = wzPackageId; | ||
| 1190 | args.messageType = messageType; | ||
| 1191 | args.dwUIHint = dwUIHint; | ||
| 1192 | args.wzMessage = wzMessage; | ||
| 1193 | args.cData = cData; | ||
| 1194 | args.rgwzData = rgwzData; | ||
| 1195 | args.nRecommendation = *pnResult; | ||
| 1196 | |||
| 1197 | results.cbSize = sizeof(results); | ||
| 1198 | results.nResult = *pnResult; | ||
| 1199 | |||
| 1200 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1201 | ExitOnFailure(hr, "BA OnExecuteMsiMessage failed."); | ||
| 1202 | |||
| 1203 | *pnResult = results.nResult; | ||
| 1204 | |||
| 1205 | LExit: | ||
| 1206 | return hr; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | EXTERN_C BAAPI UserExperienceOnExecutePackageBegin( | ||
| 1210 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1211 | __in_z LPCWSTR wzPackageId, | ||
| 1212 | __in BOOL fExecute | ||
| 1213 | ) | ||
| 1214 | { | ||
| 1215 | HRESULT hr = S_OK; | ||
| 1216 | BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { }; | ||
| 1217 | BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { }; | ||
| 1218 | |||
| 1219 | args.cbSize = sizeof(args); | ||
| 1220 | args.wzPackageId = wzPackageId; | ||
| 1221 | args.fExecute = fExecute; | ||
| 1222 | |||
| 1223 | results.cbSize = sizeof(results); | ||
| 1224 | |||
| 1225 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1226 | ExitOnFailure(hr, "BA OnExecutePackageBegin failed."); | ||
| 1227 | |||
| 1228 | if (results.fCancel) | ||
| 1229 | { | ||
| 1230 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | LExit: | ||
| 1234 | return hr; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | EXTERN_C BAAPI UserExperienceOnExecutePackageComplete( | ||
| 1238 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1239 | __in_z LPCWSTR wzPackageId, | ||
| 1240 | __in HRESULT hrStatus, | ||
| 1241 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 1242 | __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction | ||
| 1243 | ) | ||
| 1244 | { | ||
| 1245 | HRESULT hr = S_OK; | ||
| 1246 | BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { }; | ||
| 1247 | BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { }; | ||
| 1248 | |||
| 1249 | args.cbSize = sizeof(args); | ||
| 1250 | args.wzPackageId = wzPackageId; | ||
| 1251 | args.hrStatus = hrStatus; | ||
| 1252 | args.restart = restart; | ||
| 1253 | args.recommendation = *pAction; | ||
| 1254 | |||
| 1255 | results.cbSize = sizeof(results); | ||
| 1256 | results.action = *pAction; | ||
| 1257 | |||
| 1258 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1259 | ExitOnFailure(hr, "BA OnExecutePackageComplete failed."); | ||
| 1260 | |||
| 1261 | *pAction = results.action; | ||
| 1262 | |||
| 1263 | LExit: | ||
| 1264 | return hr; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | EXTERN_C BAAPI UserExperienceOnExecutePatchTarget( | ||
| 1268 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1269 | __in_z LPCWSTR wzPackageId, | ||
| 1270 | __in_z LPCWSTR wzTargetProductCode | ||
| 1271 | ) | ||
| 1272 | { | ||
| 1273 | HRESULT hr = S_OK; | ||
| 1274 | BA_ONEXECUTEPATCHTARGET_ARGS args = { }; | ||
| 1275 | BA_ONEXECUTEPATCHTARGET_RESULTS results = { }; | ||
| 1276 | |||
| 1277 | args.cbSize = sizeof(args); | ||
| 1278 | args.wzPackageId = wzPackageId; | ||
| 1279 | args.wzTargetProductCode = wzTargetProductCode; | ||
| 1280 | |||
| 1281 | results.cbSize = sizeof(results); | ||
| 1282 | |||
| 1283 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1284 | ExitOnFailure(hr, "BA OnExecutePatchTarget failed."); | ||
| 1285 | |||
| 1286 | if (results.fCancel) | ||
| 1287 | { | ||
| 1288 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | LExit: | ||
| 1292 | return hr; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | EXTERN_C BAAPI UserExperienceOnExecuteProgress( | ||
| 1296 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1297 | __in_z LPCWSTR wzPackageId, | ||
| 1298 | __in DWORD dwProgressPercentage, | ||
| 1299 | __in DWORD dwOverallPercentage, | ||
| 1300 | __out int* pnResult | ||
| 1301 | ) | ||
| 1302 | { | ||
| 1303 | HRESULT hr = S_OK; | ||
| 1304 | BA_ONEXECUTEPROGRESS_ARGS args = { }; | ||
| 1305 | BA_ONEXECUTEPROGRESS_RESULTS results = { }; | ||
| 1306 | |||
| 1307 | args.cbSize = sizeof(args); | ||
| 1308 | args.wzPackageId = wzPackageId; | ||
| 1309 | args.dwProgressPercentage = dwProgressPercentage; | ||
| 1310 | args.dwOverallPercentage = dwOverallPercentage; | ||
| 1311 | |||
| 1312 | results.cbSize = sizeof(results); | ||
| 1313 | |||
| 1314 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1315 | ExitOnFailure(hr, "BA OnExecuteProgress failed."); | ||
| 1316 | |||
| 1317 | LExit: | ||
| 1318 | if (FAILED(hr)) | ||
| 1319 | { | ||
| 1320 | *pnResult = IDERROR; | ||
| 1321 | } | ||
| 1322 | else if (results.fCancel) | ||
| 1323 | { | ||
| 1324 | *pnResult = IDCANCEL; | ||
| 1325 | } | ||
| 1326 | else | ||
| 1327 | { | ||
| 1328 | *pnResult = IDNOACTION; | ||
| 1329 | } | ||
| 1330 | return hr; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin( | ||
| 1334 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 1335 | ) | ||
| 1336 | { | ||
| 1337 | HRESULT hr = S_OK; | ||
| 1338 | BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { }; | ||
| 1339 | BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { }; | ||
| 1340 | |||
| 1341 | args.cbSize = sizeof(args); | ||
| 1342 | |||
| 1343 | results.cbSize = sizeof(results); | ||
| 1344 | |||
| 1345 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1346 | ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed."); | ||
| 1347 | |||
| 1348 | if (results.fCancel) | ||
| 1349 | { | ||
| 1350 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | LExit: | ||
| 1354 | return hr; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete( | ||
| 1358 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1359 | __in HRESULT hrStatus, | ||
| 1360 | __in DWORD dwProcessId | ||
| 1361 | ) | ||
| 1362 | { | ||
| 1363 | HRESULT hr = S_OK; | ||
| 1364 | BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { }; | ||
| 1365 | BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { }; | ||
| 1366 | |||
| 1367 | args.cbSize = sizeof(args); | ||
| 1368 | args.hrStatus = hrStatus; | ||
| 1369 | args.dwProcessId = dwProcessId; | ||
| 1370 | |||
| 1371 | results.cbSize = sizeof(results); | ||
| 1372 | |||
| 1373 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1374 | ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed."); | ||
| 1375 | |||
| 1376 | LExit: | ||
| 1377 | return hr; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | EXTERN_C BAAPI UserExperienceOnPlanBegin( | ||
| 1381 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1382 | __in DWORD cPackages | ||
| 1383 | ) | ||
| 1384 | { | ||
| 1385 | HRESULT hr = S_OK; | ||
| 1386 | BA_ONPLANBEGIN_ARGS args = { }; | ||
| 1387 | BA_ONPLANBEGIN_RESULTS results = { }; | ||
| 1388 | |||
| 1389 | args.cbSize = sizeof(args); | ||
| 1390 | args.cPackages = cPackages; | ||
| 1391 | |||
| 1392 | results.cbSize = sizeof(results); | ||
| 1393 | |||
| 1394 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1395 | ExitOnFailure(hr, "BA OnPlanBegin failed."); | ||
| 1396 | |||
| 1397 | if (results.fCancel) | ||
| 1398 | { | ||
| 1399 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | LExit: | ||
| 1403 | return hr; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( | ||
| 1407 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1408 | __in_z LPCWSTR wzPackageId, | ||
| 1409 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 1410 | __in DWORD64 dw64CompatiblePackageVersion, | ||
| 1411 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 1412 | ) | ||
| 1413 | { | ||
| 1414 | HRESULT hr = S_OK; | ||
| 1415 | BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { }; | ||
| 1416 | BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { }; | ||
| 1417 | |||
| 1418 | args.cbSize = sizeof(args); | ||
| 1419 | args.wzPackageId = wzPackageId; | ||
| 1420 | args.wzCompatiblePackageId = wzCompatiblePackageId; | ||
| 1421 | args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion; | ||
| 1422 | args.recommendedState = *pRequestedState; | ||
| 1423 | |||
| 1424 | results.cbSize = sizeof(results); | ||
| 1425 | results.requestedState = *pRequestedState; | ||
| 1426 | |||
| 1427 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1428 | ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed."); | ||
| 1429 | |||
| 1430 | if (results.fCancel) | ||
| 1431 | { | ||
| 1432 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1433 | } | ||
| 1434 | *pRequestedState = results.requestedState; | ||
| 1435 | |||
| 1436 | LExit: | ||
| 1437 | return hr; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( | ||
| 1441 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1442 | __in_z LPCWSTR wzPackageId, | ||
| 1443 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 1444 | __in HRESULT hrStatus, | ||
| 1445 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 1446 | __in BOOTSTRAPPER_REQUEST_STATE requested, | ||
| 1447 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 1448 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 1449 | ) | ||
| 1450 | { | ||
| 1451 | HRESULT hr = S_OK; | ||
| 1452 | BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { }; | ||
| 1453 | BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { }; | ||
| 1454 | |||
| 1455 | args.cbSize = sizeof(args); | ||
| 1456 | args.wzPackageId = wzPackageId; | ||
| 1457 | args.wzCompatiblePackageId = wzCompatiblePackageId; | ||
| 1458 | args.hrStatus = hrStatus; | ||
| 1459 | args.state = state; | ||
| 1460 | args.requested = requested; | ||
| 1461 | args.execute = execute; | ||
| 1462 | args.rollback = rollback; | ||
| 1463 | |||
| 1464 | results.cbSize = sizeof(results); | ||
| 1465 | |||
| 1466 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1467 | ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed."); | ||
| 1468 | |||
| 1469 | LExit: | ||
| 1470 | return hr; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( | ||
| 1474 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1475 | __in_z LPCWSTR wzPackageId, | ||
| 1476 | __in_z LPCWSTR wzFeatureId, | ||
| 1477 | __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState | ||
| 1478 | ) | ||
| 1479 | { | ||
| 1480 | HRESULT hr = S_OK; | ||
| 1481 | BA_ONPLANMSIFEATURE_ARGS args = { }; | ||
| 1482 | BA_ONPLANMSIFEATURE_RESULTS results = { }; | ||
| 1483 | |||
| 1484 | args.cbSize = sizeof(args); | ||
| 1485 | args.wzPackageId = wzPackageId; | ||
| 1486 | args.wzFeatureId = wzFeatureId; | ||
| 1487 | args.recommendedState = *pRequestedState; | ||
| 1488 | |||
| 1489 | results.cbSize = sizeof(results); | ||
| 1490 | results.requestedState = *pRequestedState; | ||
| 1491 | |||
| 1492 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1493 | ExitOnFailure(hr, "BA OnPlanMsiFeature failed."); | ||
| 1494 | |||
| 1495 | if (results.fCancel) | ||
| 1496 | { | ||
| 1497 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1498 | } | ||
| 1499 | *pRequestedState = results.requestedState; | ||
| 1500 | |||
| 1501 | LExit: | ||
| 1502 | return hr; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | EXTERN_C BAAPI UserExperienceOnPlanComplete( | ||
| 1506 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1507 | __in HRESULT hrStatus | ||
| 1508 | ) | ||
| 1509 | { | ||
| 1510 | HRESULT hr = S_OK; | ||
| 1511 | BA_ONPLANCOMPLETE_ARGS args = { }; | ||
| 1512 | BA_ONPLANCOMPLETE_RESULTS results = { }; | ||
| 1513 | |||
| 1514 | args.cbSize = sizeof(args); | ||
| 1515 | args.hrStatus = hrStatus; | ||
| 1516 | |||
| 1517 | results.cbSize = sizeof(results); | ||
| 1518 | |||
| 1519 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1520 | ExitOnFailure(hr, "BA OnPlanComplete failed."); | ||
| 1521 | |||
| 1522 | LExit: | ||
| 1523 | return hr; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | EXTERN_C BAAPI UserExperienceOnPlanPackageBegin( | ||
| 1527 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1528 | __in_z LPCWSTR wzPackageId, | ||
| 1529 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 1530 | ) | ||
| 1531 | { | ||
| 1532 | HRESULT hr = S_OK; | ||
| 1533 | BA_ONPLANPACKAGEBEGIN_ARGS args = { }; | ||
| 1534 | BA_ONPLANPACKAGEBEGIN_RESULTS results = { }; | ||
| 1535 | |||
| 1536 | args.cbSize = sizeof(args); | ||
| 1537 | args.wzPackageId = wzPackageId; | ||
| 1538 | args.recommendedState = *pRequestedState; | ||
| 1539 | |||
| 1540 | results.cbSize = sizeof(results); | ||
| 1541 | results.requestedState = *pRequestedState; | ||
| 1542 | |||
| 1543 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1544 | ExitOnFailure(hr, "BA OnPlanPackageBegin failed."); | ||
| 1545 | |||
| 1546 | if (results.fCancel) | ||
| 1547 | { | ||
| 1548 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1549 | } | ||
| 1550 | *pRequestedState = results.requestedState; | ||
| 1551 | |||
| 1552 | LExit: | ||
| 1553 | return hr; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | EXTERN_C BAAPI UserExperienceOnPlanPackageComplete( | ||
| 1557 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1558 | __in_z LPCWSTR wzPackageId, | ||
| 1559 | __in HRESULT hrStatus, | ||
| 1560 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 1561 | __in BOOTSTRAPPER_REQUEST_STATE requested, | ||
| 1562 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 1563 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 1564 | ) | ||
| 1565 | { | ||
| 1566 | HRESULT hr = S_OK; | ||
| 1567 | BA_ONPLANPACKAGECOMPLETE_ARGS args = { }; | ||
| 1568 | BA_ONPLANPACKAGECOMPLETE_RESULTS results = { }; | ||
| 1569 | |||
| 1570 | args.cbSize = sizeof(args); | ||
| 1571 | args.wzPackageId = wzPackageId; | ||
| 1572 | args.hrStatus = hrStatus; | ||
| 1573 | args.state = state; | ||
| 1574 | args.requested = requested; | ||
| 1575 | args.execute = execute; | ||
| 1576 | args.rollback = rollback; | ||
| 1577 | |||
| 1578 | results.cbSize = sizeof(results); | ||
| 1579 | |||
| 1580 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1581 | ExitOnFailure(hr, "BA OnPlanPackageComplete failed."); | ||
| 1582 | |||
| 1583 | LExit: | ||
| 1584 | return hr; | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle( | ||
| 1588 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1589 | __in_z LPCWSTR wzBundleId, | ||
| 1590 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 1591 | ) | ||
| 1592 | { | ||
| 1593 | HRESULT hr = S_OK; | ||
| 1594 | BA_ONPLANRELATEDBUNDLE_ARGS args = { }; | ||
| 1595 | BA_ONPLANRELATEDBUNDLE_RESULTS results = { }; | ||
| 1596 | |||
| 1597 | args.cbSize = sizeof(args); | ||
| 1598 | args.wzBundleId = wzBundleId; | ||
| 1599 | args.recommendedState = *pRequestedState; | ||
| 1600 | |||
| 1601 | results.cbSize = sizeof(results); | ||
| 1602 | results.requestedState = *pRequestedState; | ||
| 1603 | |||
| 1604 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1605 | ExitOnFailure(hr, "BA OnPlanRelatedBundle failed."); | ||
| 1606 | |||
| 1607 | if (results.fCancel) | ||
| 1608 | { | ||
| 1609 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1610 | } | ||
| 1611 | *pRequestedState = results.requestedState; | ||
| 1612 | |||
| 1613 | LExit: | ||
| 1614 | return hr; | ||
| 1615 | } | ||
| 1616 | |||
| 1617 | EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage( | ||
| 1618 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1619 | __in_z LPCWSTR wzPackageId, | ||
| 1620 | __in_z LPCWSTR wzProductCode, | ||
| 1621 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 1622 | ) | ||
| 1623 | { | ||
| 1624 | HRESULT hr = S_OK; | ||
| 1625 | BA_ONPLANTARGETMSIPACKAGE_ARGS args = { }; | ||
| 1626 | BA_ONPLANTARGETMSIPACKAGE_RESULTS results = { }; | ||
| 1627 | |||
| 1628 | args.cbSize = sizeof(args); | ||
| 1629 | args.wzPackageId = wzPackageId; | ||
| 1630 | args.wzProductCode = wzProductCode; | ||
| 1631 | args.recommendedState = *pRequestedState; | ||
| 1632 | |||
| 1633 | results.cbSize = sizeof(results); | ||
| 1634 | results.requestedState = *pRequestedState; | ||
| 1635 | |||
| 1636 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1637 | ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed."); | ||
| 1638 | |||
| 1639 | if (results.fCancel) | ||
| 1640 | { | ||
| 1641 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1642 | } | ||
| 1643 | *pRequestedState = results.requestedState; | ||
| 1644 | |||
| 1645 | LExit: | ||
| 1646 | return hr; | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | EXTERN_C BAAPI UserExperienceOnProgress( | ||
| 1650 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1651 | __in BOOL fRollback, | ||
| 1652 | __in DWORD dwProgressPercentage, | ||
| 1653 | __in DWORD dwOverallPercentage | ||
| 1654 | ) | ||
| 1655 | { | ||
| 1656 | HRESULT hr = S_OK; | ||
| 1657 | BA_ONPROGRESS_ARGS args = { }; | ||
| 1658 | BA_ONPROGRESS_RESULTS results = { }; | ||
| 1659 | |||
| 1660 | args.cbSize = sizeof(args); | ||
| 1661 | args.dwProgressPercentage = dwProgressPercentage; | ||
| 1662 | args.dwOverallPercentage = dwOverallPercentage; | ||
| 1663 | |||
| 1664 | results.cbSize = sizeof(results); | ||
| 1665 | |||
| 1666 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1667 | hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress"); | ||
| 1668 | |||
| 1669 | return hr; | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | EXTERN_C BAAPI UserExperienceOnRegisterBegin( | ||
| 1673 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 1674 | ) | ||
| 1675 | { | ||
| 1676 | HRESULT hr = S_OK; | ||
| 1677 | BA_ONREGISTERBEGIN_ARGS args = { }; | ||
| 1678 | BA_ONREGISTERBEGIN_RESULTS results = { }; | ||
| 1679 | |||
| 1680 | args.cbSize = sizeof(args); | ||
| 1681 | |||
| 1682 | results.cbSize = sizeof(results); | ||
| 1683 | |||
| 1684 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1685 | ExitOnFailure(hr, "BA OnRegisterBegin failed."); | ||
| 1686 | |||
| 1687 | if (results.fCancel) | ||
| 1688 | { | ||
| 1689 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | LExit: | ||
| 1693 | return hr; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | EXTERN_C BAAPI UserExperienceOnRegisterComplete( | ||
| 1697 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1698 | __in HRESULT hrStatus | ||
| 1699 | ) | ||
| 1700 | { | ||
| 1701 | HRESULT hr = S_OK; | ||
| 1702 | BA_ONREGISTERCOMPLETE_ARGS args = { }; | ||
| 1703 | BA_ONREGISTERCOMPLETE_RESULTS results = { }; | ||
| 1704 | |||
| 1705 | args.cbSize = sizeof(args); | ||
| 1706 | args.hrStatus = hrStatus; | ||
| 1707 | |||
| 1708 | results.cbSize = sizeof(results); | ||
| 1709 | |||
| 1710 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1711 | ExitOnFailure(hr, "BA OnRegisterComplete failed."); | ||
| 1712 | |||
| 1713 | LExit: | ||
| 1714 | return hr; | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | EXTERN_C BAAPI UserExperienceOnResolveSource( | ||
| 1718 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1719 | __in_z LPCWSTR wzPackageOrContainerId, | ||
| 1720 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 1721 | __in_z LPCWSTR wzLocalSource, | ||
| 1722 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 1723 | __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction | ||
| 1724 | ) | ||
| 1725 | { | ||
| 1726 | HRESULT hr = S_OK; | ||
| 1727 | BA_ONRESOLVESOURCE_ARGS args = { }; | ||
| 1728 | BA_ONRESOLVESOURCE_RESULTS results = { }; | ||
| 1729 | |||
| 1730 | args.cbSize = sizeof(args); | ||
| 1731 | args.wzPackageOrContainerId = wzPackageOrContainerId; | ||
| 1732 | args.wzPayloadId = wzPayloadId; | ||
| 1733 | args.wzLocalSource = wzLocalSource; | ||
| 1734 | args.wzDownloadSource = wzDownloadSource; | ||
| 1735 | args.recommendation = *pAction; | ||
| 1736 | |||
| 1737 | results.cbSize = sizeof(results); | ||
| 1738 | results.action = *pAction; | ||
| 1739 | |||
| 1740 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1741 | ExitOnFailure(hr, "BA OnResolveSource failed."); | ||
| 1742 | |||
| 1743 | if (results.fCancel) | ||
| 1744 | { | ||
| 1745 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1746 | } | ||
| 1747 | else | ||
| 1748 | { | ||
| 1749 | *pAction = results.action; | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | LExit: | ||
| 1753 | return hr; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | EXTERN_C BAAPI UserExperienceOnShutdown( | ||
| 1757 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1758 | __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction | ||
| 1759 | ) | ||
| 1760 | { | ||
| 1761 | HRESULT hr = S_OK; | ||
| 1762 | BA_ONSHUTDOWN_ARGS args = { }; | ||
| 1763 | BA_ONSHUTDOWN_RESULTS results = { }; | ||
| 1764 | |||
| 1765 | args.cbSize = sizeof(args); | ||
| 1766 | |||
| 1767 | results.cbSize = sizeof(results); | ||
| 1768 | results.action = *pAction; | ||
| 1769 | |||
| 1770 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1771 | ExitOnFailure(hr, "BA OnShutdown failed."); | ||
| 1772 | |||
| 1773 | *pAction = results.action; | ||
| 1774 | |||
| 1775 | LExit: | ||
| 1776 | return hr; | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | EXTERN_C BAAPI UserExperienceOnStartup( | ||
| 1780 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 1781 | ) | ||
| 1782 | { | ||
| 1783 | HRESULT hr = S_OK; | ||
| 1784 | BA_ONSTARTUP_ARGS args = { }; | ||
| 1785 | BA_ONSTARTUP_RESULTS results = { }; | ||
| 1786 | |||
| 1787 | args.cbSize = sizeof(args); | ||
| 1788 | |||
| 1789 | results.cbSize = sizeof(results); | ||
| 1790 | |||
| 1791 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1792 | ExitOnFailure(hr, "BA OnStartup failed."); | ||
| 1793 | |||
| 1794 | LExit: | ||
| 1795 | return hr; | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | EXTERN_C BAAPI UserExperienceOnSystemShutdown( | ||
| 1799 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1800 | __in DWORD dwEndSession, | ||
| 1801 | __inout BOOL* pfCancel | ||
| 1802 | ) | ||
| 1803 | { | ||
| 1804 | HRESULT hr = S_OK; | ||
| 1805 | BA_ONSYSTEMSHUTDOWN_ARGS args = { }; | ||
| 1806 | BA_ONSYSTEMSHUTDOWN_RESULTS results = { }; | ||
| 1807 | |||
| 1808 | args.cbSize = sizeof(args); | ||
| 1809 | args.dwEndSession = dwEndSession; | ||
| 1810 | |||
| 1811 | results.cbSize = sizeof(results); | ||
| 1812 | results.fCancel = *pfCancel; | ||
| 1813 | |||
| 1814 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1815 | ExitOnFailure(hr, "BA OnSystemShutdown failed."); | ||
| 1816 | |||
| 1817 | *pfCancel = results.fCancel; | ||
| 1818 | |||
| 1819 | LExit: | ||
| 1820 | return hr; | ||
| 1821 | } | ||
| 1822 | |||
| 1823 | EXTERN_C BAAPI UserExperienceOnUnregisterBegin( | ||
| 1824 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 1825 | ) | ||
| 1826 | { | ||
| 1827 | HRESULT hr = S_OK; | ||
| 1828 | BA_ONUNREGISTERBEGIN_ARGS args = { }; | ||
| 1829 | BA_ONUNREGISTERBEGIN_RESULTS results = { }; | ||
| 1830 | |||
| 1831 | args.cbSize = sizeof(args); | ||
| 1832 | |||
| 1833 | results.cbSize = sizeof(results); | ||
| 1834 | |||
| 1835 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1836 | ExitOnFailure(hr, "BA OnUnregisterBegin failed."); | ||
| 1837 | |||
| 1838 | if (results.fCancel) | ||
| 1839 | { | ||
| 1840 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | LExit: | ||
| 1844 | return hr; | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | EXTERN_C BAAPI UserExperienceOnUnregisterComplete( | ||
| 1848 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1849 | __in HRESULT hrStatus | ||
| 1850 | ) | ||
| 1851 | { | ||
| 1852 | HRESULT hr = S_OK; | ||
| 1853 | BA_ONUNREGISTERCOMPLETE_ARGS args = { }; | ||
| 1854 | BA_ONUNREGISTERCOMPLETE_RESULTS results = { }; | ||
| 1855 | |||
| 1856 | args.cbSize = sizeof(args); | ||
| 1857 | args.hrStatus = hrStatus; | ||
| 1858 | |||
| 1859 | results.cbSize = sizeof(results); | ||
| 1860 | |||
| 1861 | hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext); | ||
| 1862 | ExitOnFailure(hr, "BA OnUnregisterComplete failed."); | ||
| 1863 | |||
| 1864 | LExit: | ||
| 1865 | return hr; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | extern "C" int UserExperienceCheckExecuteResult( | ||
| 1869 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1870 | __in BOOL fRollback, | ||
| 1871 | __in DWORD dwAllowedResults, | ||
| 1872 | __in int nResult | ||
| 1873 | ) | ||
| 1874 | { | ||
| 1875 | // Do not allow canceling while rolling back. | ||
| 1876 | if (fRollback && (IDCANCEL == nResult || IDABORT == nResult)) | ||
| 1877 | { | ||
| 1878 | nResult = IDNOACTION; | ||
| 1879 | } | ||
| 1880 | else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback. | ||
| 1881 | { | ||
| 1882 | nResult = IDCANCEL; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | nResult = FilterResult(dwAllowedResults, nResult); | ||
| 1886 | return nResult; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | extern "C" HRESULT UserExperienceInterpretResult( | ||
| 1890 | __in BURN_USER_EXPERIENCE* /*pUserExperience*/, | ||
| 1891 | __in DWORD dwAllowedResults, | ||
| 1892 | __in int nResult | ||
| 1893 | ) | ||
| 1894 | { | ||
| 1895 | int nFilteredResult = FilterResult(dwAllowedResults, nResult); | ||
| 1896 | return IDOK == nFilteredResult || IDNOACTION == nFilteredResult ? S_OK : IDCANCEL == nFilteredResult || IDABORT == nFilteredResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | extern "C" HRESULT UserExperienceInterpretExecuteResult( | ||
| 1900 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 1901 | __in BOOL fRollback, | ||
| 1902 | __in DWORD dwAllowedResults, | ||
| 1903 | __in int nResult | ||
| 1904 | ) | ||
| 1905 | { | ||
| 1906 | HRESULT hr = S_OK; | ||
| 1907 | |||
| 1908 | // If we failed return that error unless this is rollback which should roll on. | ||
| 1909 | if (FAILED(pUserExperience->hrApplyError) && !fRollback) | ||
| 1910 | { | ||
| 1911 | hr = pUserExperience->hrApplyError; | ||
| 1912 | } | ||
| 1913 | else | ||
| 1914 | { | ||
| 1915 | int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult); | ||
| 1916 | hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
| 1917 | } | ||
| 1918 | |||
| 1919 | return hr; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | |||
| 1923 | // internal functions | ||
| 1924 | |||
| 1925 | static int FilterResult( | ||
| 1926 | __in DWORD dwAllowedResults, | ||
| 1927 | __in int nResult | ||
| 1928 | ) | ||
| 1929 | { | ||
| 1930 | if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through. | ||
| 1931 | { | ||
| 1932 | } | ||
| 1933 | else | ||
| 1934 | { | ||
| 1935 | switch (dwAllowedResults) | ||
| 1936 | { | ||
| 1937 | case MB_OK: | ||
| 1938 | nResult = IDOK; | ||
| 1939 | break; | ||
| 1940 | |||
| 1941 | case MB_OKCANCEL: | ||
| 1942 | if (IDOK == nResult || IDYES == nResult) | ||
| 1943 | { | ||
| 1944 | nResult = IDOK; | ||
| 1945 | } | ||
| 1946 | else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) | ||
| 1947 | { | ||
| 1948 | nResult = IDCANCEL; | ||
| 1949 | } | ||
| 1950 | else | ||
| 1951 | { | ||
| 1952 | nResult = IDNOACTION; | ||
| 1953 | } | ||
| 1954 | break; | ||
| 1955 | |||
| 1956 | case MB_ABORTRETRYIGNORE: | ||
| 1957 | if (IDCANCEL == nResult || IDABORT == nResult) | ||
| 1958 | { | ||
| 1959 | nResult = IDABORT; | ||
| 1960 | } | ||
| 1961 | else if (IDRETRY == nResult || IDTRYAGAIN == nResult) | ||
| 1962 | { | ||
| 1963 | nResult = IDRETRY; | ||
| 1964 | } | ||
| 1965 | else if (IDIGNORE == nResult) | ||
| 1966 | { | ||
| 1967 | nResult = IDIGNORE; | ||
| 1968 | } | ||
| 1969 | else | ||
| 1970 | { | ||
| 1971 | nResult = IDNOACTION; | ||
| 1972 | } | ||
| 1973 | break; | ||
| 1974 | |||
| 1975 | case MB_YESNO: | ||
| 1976 | if (IDOK == nResult || IDYES == nResult) | ||
| 1977 | { | ||
| 1978 | nResult = IDYES; | ||
| 1979 | } | ||
| 1980 | else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult) | ||
| 1981 | { | ||
| 1982 | nResult = IDNO; | ||
| 1983 | } | ||
| 1984 | else | ||
| 1985 | { | ||
| 1986 | nResult = IDNOACTION; | ||
| 1987 | } | ||
| 1988 | break; | ||
| 1989 | |||
| 1990 | case MB_YESNOCANCEL: | ||
| 1991 | if (IDOK == nResult || IDYES == nResult) | ||
| 1992 | { | ||
| 1993 | nResult = IDYES; | ||
| 1994 | } | ||
| 1995 | else if (IDNO == nResult) | ||
| 1996 | { | ||
| 1997 | nResult = IDNO; | ||
| 1998 | } | ||
| 1999 | else if (IDCANCEL == nResult || IDABORT == nResult) | ||
| 2000 | { | ||
| 2001 | nResult = IDCANCEL; | ||
| 2002 | } | ||
| 2003 | else | ||
| 2004 | { | ||
| 2005 | nResult = IDNOACTION; | ||
| 2006 | } | ||
| 2007 | break; | ||
| 2008 | |||
| 2009 | case MB_RETRYCANCEL: | ||
| 2010 | if (IDRETRY == nResult || IDTRYAGAIN == nResult) | ||
| 2011 | { | ||
| 2012 | nResult = IDRETRY; | ||
| 2013 | } | ||
| 2014 | else if (IDCANCEL == nResult || IDABORT == nResult) | ||
| 2015 | { | ||
| 2016 | nResult = IDABORT; | ||
| 2017 | } | ||
| 2018 | else | ||
| 2019 | { | ||
| 2020 | nResult = IDNOACTION; | ||
| 2021 | } | ||
| 2022 | break; | ||
| 2023 | |||
| 2024 | case MB_CANCELTRYCONTINUE: | ||
| 2025 | if (IDCANCEL == nResult || IDABORT == nResult) | ||
| 2026 | { | ||
| 2027 | nResult = IDABORT; | ||
| 2028 | } | ||
| 2029 | else if (IDRETRY == nResult || IDTRYAGAIN == nResult) | ||
| 2030 | { | ||
| 2031 | nResult = IDRETRY; | ||
| 2032 | } | ||
| 2033 | else if (IDCONTINUE == nResult || IDIGNORE == nResult) | ||
| 2034 | { | ||
| 2035 | nResult = IDCONTINUE; | ||
| 2036 | } | ||
| 2037 | else | ||
| 2038 | { | ||
| 2039 | nResult = IDNOACTION; | ||
| 2040 | } | ||
| 2041 | break; | ||
| 2042 | |||
| 2043 | case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code. | ||
| 2044 | if (IDOK == nResult || IDYES == nResult) | ||
| 2045 | { | ||
| 2046 | nResult = IDOK; | ||
| 2047 | } | ||
| 2048 | else if (IDCONTINUE == nResult || IDIGNORE == nResult) | ||
| 2049 | { | ||
| 2050 | nResult = IDIGNORE; | ||
| 2051 | } | ||
| 2052 | else if (IDCANCEL == nResult || IDABORT == nResult) | ||
| 2053 | { | ||
| 2054 | nResult = IDCANCEL; | ||
| 2055 | } | ||
| 2056 | else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult) | ||
| 2057 | { | ||
| 2058 | nResult = IDRETRY; | ||
| 2059 | } | ||
| 2060 | else | ||
| 2061 | { | ||
| 2062 | nResult = IDNOACTION; | ||
| 2063 | } | ||
| 2064 | break; | ||
| 2065 | |||
| 2066 | case MB_RETRYTRYAGAIN: // custom return code. | ||
| 2067 | if (IDRETRY != nResult && IDTRYAGAIN != nResult) | ||
| 2068 | { | ||
| 2069 | nResult = IDNOACTION; | ||
| 2070 | } | ||
| 2071 | break; | ||
| 2072 | |||
| 2073 | default: | ||
| 2074 | AssertSz(FALSE, "Unknown allowed results."); | ||
| 2075 | break; | ||
| 2076 | } | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | return nResult; | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | // This filters the BA's responses to events during apply. | ||
| 2083 | // If an apply thread failed, then return its error so this thread will bail out. | ||
| 2084 | // During rollback, the BA can't cancel. | ||
| 2085 | static HRESULT FilterExecuteResult( | ||
| 2086 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 2087 | __in HRESULT hrStatus, | ||
| 2088 | __in BOOL fRollback, | ||
| 2089 | __in BOOL fCancel, | ||
| 2090 | __in LPCWSTR sczEventName | ||
| 2091 | ) | ||
| 2092 | { | ||
| 2093 | HRESULT hr = hrStatus; | ||
| 2094 | HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads. | ||
| 2095 | |||
| 2096 | // If we failed return that error unless this is rollback which should roll on. | ||
| 2097 | if (FAILED(hrApplyError) && !fRollback) | ||
| 2098 | { | ||
| 2099 | hr = hrApplyError; | ||
| 2100 | } | ||
| 2101 | else if (fRollback) | ||
| 2102 | { | ||
| 2103 | if (fCancel) | ||
| 2104 | { | ||
| 2105 | LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName); | ||
| 2106 | } | ||
| 2107 | // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well? | ||
| 2108 | // In the previous code, they could still alter rollback by returning IDERROR. | ||
| 2109 | } | ||
| 2110 | else | ||
| 2111 | { | ||
| 2112 | ExitOnFailure(hr, "BA %ls failed.", sczEventName); | ||
| 2113 | |||
| 2114 | if (fCancel) | ||
| 2115 | { | ||
| 2116 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
| 2117 | } | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | LExit: | ||
| 2121 | return hr; | ||
| 2122 | } | ||
diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h new file mode 100644 index 00000000..27a94115 --- /dev/null +++ b/src/engine/userexperience.h | |||
| @@ -0,0 +1,439 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | #define BAAPI HRESULT __stdcall | ||
| 5 | |||
| 6 | #if defined(__cplusplus) | ||
| 7 | extern "C" { | ||
| 8 | #endif | ||
| 9 | |||
| 10 | |||
| 11 | // constants | ||
| 12 | |||
| 13 | const DWORD MB_RETRYTRYAGAIN = 0xF; | ||
| 14 | |||
| 15 | |||
| 16 | // structs | ||
| 17 | |||
| 18 | struct BOOTSTRAPPER_ENGINE_CONTEXT; | ||
| 19 | |||
| 20 | typedef struct _BURN_USER_EXPERIENCE | ||
| 21 | { | ||
| 22 | BOOL fSplashScreen; | ||
| 23 | BURN_PAYLOADS payloads; | ||
| 24 | |||
| 25 | HMODULE hUXModule; | ||
| 26 | PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc; | ||
| 27 | LPVOID pvBAProcContext; | ||
| 28 | LPWSTR sczTempDirectory; | ||
| 29 | |||
| 30 | CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be | ||
| 31 | // syncronized through this critical section. | ||
| 32 | // Note: The engine must never do a UX callback while in this critical section. | ||
| 33 | |||
| 34 | BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution | ||
| 35 | // steps (detect, plan, apply), and cannot accept requests from the UX. | ||
| 36 | // This flag should be cleared by the engine prior to UX callbacks that | ||
| 37 | // allows altering of the engine state. | ||
| 38 | |||
| 39 | HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or | ||
| 40 | // execute threads to bail. | ||
| 41 | |||
| 42 | HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid | ||
| 43 | // during apply. | ||
| 44 | |||
| 45 | HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid | ||
| 46 | // during Detect. | ||
| 47 | |||
| 48 | DWORD dwExitCode; // Exit code returned by the user experience for the engine overall. | ||
| 49 | } BURN_USER_EXPERIENCE; | ||
| 50 | |||
| 51 | // functions | ||
| 52 | |||
| 53 | HRESULT UserExperienceParseFromXml( | ||
| 54 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 55 | __in IXMLDOMNode* pixnBundle | ||
| 56 | ); | ||
| 57 | void UserExperienceUninitialize( | ||
| 58 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 59 | ); | ||
| 60 | HRESULT UserExperienceLoad( | ||
| 61 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 62 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, | ||
| 63 | __in BOOTSTRAPPER_COMMAND* pCommand | ||
| 64 | ); | ||
| 65 | HRESULT UserExperienceUnload( | ||
| 66 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 67 | ); | ||
| 68 | HRESULT UserExperienceEnsureWorkingFolder( | ||
| 69 | __in LPCWSTR wzBundleId, | ||
| 70 | __deref_out_z LPWSTR* psczUserExperienceWorkingFolder | ||
| 71 | ); | ||
| 72 | HRESULT UserExperienceRemove( | ||
| 73 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 74 | ); | ||
| 75 | int UserExperienceSendError( | ||
| 76 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 77 | __in BOOTSTRAPPER_ERROR_TYPE errorType, | ||
| 78 | __in_z_opt LPCWSTR wzPackageId, | ||
| 79 | __in HRESULT hrCode, | ||
| 80 | __in_z_opt LPCWSTR wzError, | ||
| 81 | __in DWORD uiFlags, | ||
| 82 | __in int nRecommendation | ||
| 83 | ); | ||
| 84 | HRESULT UserExperienceActivateEngine( | ||
| 85 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 86 | __out_opt BOOL* pfActivated | ||
| 87 | ); | ||
| 88 | void UserExperienceDeactivateEngine( | ||
| 89 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 90 | ); | ||
| 91 | HRESULT UserExperienceEnsureEngineInactive( | ||
| 92 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 93 | ); | ||
| 94 | void UserExperienceExecuteReset( | ||
| 95 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 96 | ); | ||
| 97 | void UserExperienceExecutePhaseComplete( | ||
| 98 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 99 | __in HRESULT hrResult | ||
| 100 | ); | ||
| 101 | BAAPI UserExperienceOnApplyBegin( | ||
| 102 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 103 | __in DWORD dwPhaseCount | ||
| 104 | ); | ||
| 105 | BAAPI UserExperienceOnApplyComplete( | ||
| 106 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 107 | __in HRESULT hrStatus, | ||
| 108 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 109 | __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction | ||
| 110 | ); | ||
| 111 | BAAPI UserExperienceOnCacheAcquireBegin( | ||
| 112 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 113 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 114 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 115 | __in BOOTSTRAPPER_CACHE_OPERATION operation, | ||
| 116 | __in_z LPCWSTR wzSource | ||
| 117 | ); | ||
| 118 | BAAPI UserExperienceOnCacheAcquireComplete( | ||
| 119 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 120 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 121 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 122 | __in HRESULT hrStatus, | ||
| 123 | __inout BOOL* pfRetry | ||
| 124 | ); | ||
| 125 | BAAPI UserExperienceOnCacheAcquireProgress( | ||
| 126 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 127 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 128 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 129 | __in DWORD64 dw64Progress, | ||
| 130 | __in DWORD64 dw64Total, | ||
| 131 | __in DWORD dwOverallPercentage | ||
| 132 | ); | ||
| 133 | BAAPI UserExperienceOnCacheBegin( | ||
| 134 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 135 | ); | ||
| 136 | BAAPI UserExperienceOnCacheComplete( | ||
| 137 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 138 | __in HRESULT hrStatus | ||
| 139 | ); | ||
| 140 | BAAPI UserExperienceOnCachePackageBegin( | ||
| 141 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 142 | __in_z LPCWSTR wzPackageId, | ||
| 143 | __in DWORD cCachePayloads, | ||
| 144 | __in DWORD64 dw64PackageCacheSize | ||
| 145 | ); | ||
| 146 | BAAPI UserExperienceOnCachePackageComplete( | ||
| 147 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 148 | __in_z LPCWSTR wzPackageId, | ||
| 149 | __in HRESULT hrStatus, | ||
| 150 | __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction | ||
| 151 | ); | ||
| 152 | BAAPI UserExperienceOnCacheVerifyBegin( | ||
| 153 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 154 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 155 | __in_z_opt LPCWSTR wzPayloadId | ||
| 156 | ); | ||
| 157 | BAAPI UserExperienceOnCacheVerifyComplete( | ||
| 158 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 159 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
| 160 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 161 | __in HRESULT hrStatus, | ||
| 162 | __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction | ||
| 163 | ); | ||
| 164 | BAAPI UserExperienceOnDetectBegin( | ||
| 165 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 166 | __in BOOL fInstalled, | ||
| 167 | __in DWORD cPackages | ||
| 168 | ); | ||
| 169 | BAAPI UserExperienceOnDetectCompatibleMsiPackage( | ||
| 170 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 171 | __in_z LPCWSTR wzPackageId, | ||
| 172 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 173 | __in DWORD64 dw64CompatiblePackageVersion | ||
| 174 | ); | ||
| 175 | BAAPI UserExperienceOnDetectComplete( | ||
| 176 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 177 | __in HRESULT hrStatus | ||
| 178 | ); | ||
| 179 | BAAPI UserExperienceOnDetectForwardCompatibleBundle( | ||
| 180 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 181 | __in_z LPCWSTR wzBundleId, | ||
| 182 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 183 | __in_z LPCWSTR wzBundleTag, | ||
| 184 | __in BOOL fPerMachine, | ||
| 185 | __in DWORD64 dw64Version, | ||
| 186 | __inout BOOL* pfIgnoreBundle | ||
| 187 | ); | ||
| 188 | BAAPI UserExperienceOnDetectMsiFeature( | ||
| 189 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 190 | __in_z LPCWSTR wzPackageId, | ||
| 191 | __in_z LPCWSTR wzFeatureId, | ||
| 192 | __in BOOTSTRAPPER_FEATURE_STATE state | ||
| 193 | ); | ||
| 194 | BAAPI UserExperienceOnDetectPackageBegin( | ||
| 195 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 196 | __in_z LPCWSTR wzPackageId | ||
| 197 | ); | ||
| 198 | BAAPI UserExperienceOnDetectPackageComplete( | ||
| 199 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 200 | __in_z LPCWSTR wzPackageId, | ||
| 201 | __in HRESULT hrStatus, | ||
| 202 | __in BOOTSTRAPPER_PACKAGE_STATE state | ||
| 203 | ); | ||
| 204 | BAAPI UserExperienceOnDetectRelatedBundle( | ||
| 205 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 206 | __in_z LPCWSTR wzBundleId, | ||
| 207 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
| 208 | __in_z LPCWSTR wzBundleTag, | ||
| 209 | __in BOOL fPerMachine, | ||
| 210 | __in DWORD64 dw64Version, | ||
| 211 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 212 | ); | ||
| 213 | BAAPI UserExperienceOnDetectRelatedMsiPackage( | ||
| 214 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 215 | __in_z LPCWSTR wzPackageId, | ||
| 216 | __in_z LPCWSTR wzUpgradeCode, | ||
| 217 | __in_z LPCWSTR wzProductCode, | ||
| 218 | __in BOOL fPerMachine, | ||
| 219 | __in DWORD64 dw64Version, | ||
| 220 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
| 221 | ); | ||
| 222 | BAAPI UserExperienceOnDetectTargetMsiPackage( | ||
| 223 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 224 | __in_z LPCWSTR wzPackageId, | ||
| 225 | __in_z LPCWSTR wzProductCode, | ||
| 226 | __in BOOTSTRAPPER_PACKAGE_STATE patchState | ||
| 227 | ); | ||
| 228 | BAAPI UserExperienceOnDetectUpdate( | ||
| 229 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 230 | __in_z LPCWSTR wzUpdateLocation, | ||
| 231 | __in DWORD64 dw64Size, | ||
| 232 | __in DWORD64 dw64Version, | ||
| 233 | __in_z_opt LPCWSTR wzTitle, | ||
| 234 | __in_z_opt LPCWSTR wzSummary, | ||
| 235 | __in_z_opt LPCWSTR wzContentType, | ||
| 236 | __in_z_opt LPCWSTR wzContent, | ||
| 237 | __inout BOOL* pfStopProcessingUpdates | ||
| 238 | ); | ||
| 239 | BAAPI UserExperienceOnDetectUpdateBegin( | ||
| 240 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 241 | __in_z LPCWSTR wzUpdateLocation, | ||
| 242 | __inout BOOL* pfSkip | ||
| 243 | ); | ||
| 244 | BAAPI UserExperienceOnDetectUpdateComplete( | ||
| 245 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 246 | __in HRESULT hrStatus, | ||
| 247 | __inout BOOL* pfIgnoreError | ||
| 248 | ); | ||
| 249 | BAAPI UserExperienceOnElevateBegin( | ||
| 250 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 251 | ); | ||
| 252 | BAAPI UserExperienceOnElevateComplete( | ||
| 253 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 254 | __in HRESULT hrStatus | ||
| 255 | ); | ||
| 256 | BAAPI UserExperienceOnError( | ||
| 257 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 258 | __in BOOTSTRAPPER_ERROR_TYPE errorType, | ||
| 259 | __in_z_opt LPCWSTR wzPackageId, | ||
| 260 | __in DWORD dwCode, | ||
| 261 | __in_z_opt LPCWSTR wzError, | ||
| 262 | __in DWORD dwUIHint, | ||
| 263 | __in DWORD cData, | ||
| 264 | __in_ecount_z_opt(cData) LPCWSTR* rgwzData, | ||
| 265 | __inout int* pnResult | ||
| 266 | ); | ||
| 267 | BAAPI UserExperienceOnExecuteBegin( | ||
| 268 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 269 | __in DWORD cExecutingPackages | ||
| 270 | ); | ||
| 271 | BAAPI UserExperienceOnExecuteComplete( | ||
| 272 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 273 | __in HRESULT hrStatus | ||
| 274 | ); | ||
| 275 | BAAPI UserExperienceOnExecuteFilesInUse( | ||
| 276 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 277 | __in_z LPCWSTR wzPackageId, | ||
| 278 | __in DWORD cFiles, | ||
| 279 | __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles, | ||
| 280 | __inout int* pnResult | ||
| 281 | ); | ||
| 282 | BAAPI UserExperienceOnExecuteMsiMessage( | ||
| 283 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 284 | __in_z LPCWSTR wzPackageId, | ||
| 285 | __in INSTALLMESSAGE messageType, | ||
| 286 | __in DWORD dwUIHint, | ||
| 287 | __in_z LPCWSTR wzMessage, | ||
| 288 | __in DWORD cData, | ||
| 289 | __in_ecount_z_opt(cData) LPCWSTR* rgwzData, | ||
| 290 | __inout int* pnResult | ||
| 291 | ); | ||
| 292 | BAAPI UserExperienceOnExecutePackageBegin( | ||
| 293 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 294 | __in_z LPCWSTR wzPackageId, | ||
| 295 | __in BOOL fExecute | ||
| 296 | ); | ||
| 297 | BAAPI UserExperienceOnExecutePackageComplete( | ||
| 298 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 299 | __in_z LPCWSTR wzPackageId, | ||
| 300 | __in HRESULT hrStatus, | ||
| 301 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
| 302 | __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction | ||
| 303 | ); | ||
| 304 | BAAPI UserExperienceOnExecutePatchTarget( | ||
| 305 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 306 | __in_z LPCWSTR wzPackageId, | ||
| 307 | __in_z LPCWSTR wzTargetProductCode | ||
| 308 | ); | ||
| 309 | BAAPI UserExperienceOnExecuteProgress( | ||
| 310 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 311 | __in_z LPCWSTR wzPackageId, | ||
| 312 | __in DWORD dwProgressPercentage, | ||
| 313 | __in DWORD dwOverallPercentage, | ||
| 314 | __inout int* pnResult | ||
| 315 | ); | ||
| 316 | BAAPI UserExperienceOnLaunchApprovedExeBegin( | ||
| 317 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 318 | ); | ||
| 319 | BAAPI UserExperienceOnLaunchApprovedExeComplete( | ||
| 320 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 321 | __in HRESULT hrStatus, | ||
| 322 | __in DWORD dwProcessId | ||
| 323 | ); | ||
| 324 | BAAPI UserExperienceOnPlanBegin( | ||
| 325 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 326 | __in DWORD cPackages | ||
| 327 | ); | ||
| 328 | BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin( | ||
| 329 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 330 | __in_z LPCWSTR wzPackageId, | ||
| 331 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 332 | __in DWORD64 dw64CompatiblePackageVersion, | ||
| 333 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 334 | ); | ||
| 335 | BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete( | ||
| 336 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 337 | __in_z LPCWSTR wzPackageId, | ||
| 338 | __in_z LPCWSTR wzCompatiblePackageId, | ||
| 339 | __in HRESULT hrStatus, | ||
| 340 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 341 | __in BOOTSTRAPPER_REQUEST_STATE requested, | ||
| 342 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 343 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 344 | ); | ||
| 345 | BAAPI UserExperienceOnPlanComplete( | ||
| 346 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 347 | __in HRESULT hrStatus | ||
| 348 | ); | ||
| 349 | BAAPI UserExperienceOnPlanMsiFeature( | ||
| 350 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 351 | __in_z LPCWSTR wzPackageId, | ||
| 352 | __in_z LPCWSTR wzFeatureId, | ||
| 353 | __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState | ||
| 354 | ); | ||
| 355 | BAAPI UserExperienceOnPlanPackageBegin( | ||
| 356 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 357 | __in_z LPCWSTR wzPackageId, | ||
| 358 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 359 | ); | ||
| 360 | BAAPI UserExperienceOnPlanPackageComplete( | ||
| 361 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 362 | __in_z LPCWSTR wzPackageId, | ||
| 363 | __in HRESULT hrStatus, | ||
| 364 | __in BOOTSTRAPPER_PACKAGE_STATE state, | ||
| 365 | __in BOOTSTRAPPER_REQUEST_STATE requested, | ||
| 366 | __in BOOTSTRAPPER_ACTION_STATE execute, | ||
| 367 | __in BOOTSTRAPPER_ACTION_STATE rollback | ||
| 368 | ); | ||
| 369 | BAAPI UserExperienceOnPlanRelatedBundle( | ||
| 370 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 371 | __in_z LPCWSTR wzBundleId, | ||
| 372 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 373 | ); | ||
| 374 | BAAPI UserExperienceOnPlanTargetMsiPackage( | ||
| 375 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 376 | __in_z LPCWSTR wzPackageId, | ||
| 377 | __in_z LPCWSTR wzProductCode, | ||
| 378 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
| 379 | ); | ||
| 380 | BAAPI UserExperienceOnProgress( | ||
| 381 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 382 | __in BOOL fRollback, | ||
| 383 | __in DWORD dwProgressPercentage, | ||
| 384 | __in DWORD dwOverallPercentage | ||
| 385 | ); | ||
| 386 | BAAPI UserExperienceOnRegisterBegin( | ||
| 387 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 388 | ); | ||
| 389 | BAAPI UserExperienceOnRegisterComplete( | ||
| 390 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 391 | __in HRESULT hrStatus | ||
| 392 | ); | ||
| 393 | BAAPI UserExperienceOnResolveSource( | ||
| 394 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 395 | __in_z LPCWSTR wzPackageOrContainerId, | ||
| 396 | __in_z_opt LPCWSTR wzPayloadId, | ||
| 397 | __in_z LPCWSTR wzLocalSource, | ||
| 398 | __in_z_opt LPCWSTR wzDownloadSource, | ||
| 399 | __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction | ||
| 400 | ); | ||
| 401 | BAAPI UserExperienceOnShutdown( | ||
| 402 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 403 | __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction | ||
| 404 | ); | ||
| 405 | BAAPI UserExperienceOnStartup( | ||
| 406 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 407 | ); | ||
| 408 | BAAPI UserExperienceOnSystemShutdown( | ||
| 409 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 410 | __in DWORD dwEndSession, | ||
| 411 | __inout BOOL* pfCancel | ||
| 412 | ); | ||
| 413 | BAAPI UserExperienceOnUnregisterBegin( | ||
| 414 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
| 415 | ); | ||
| 416 | BAAPI UserExperienceOnUnregisterComplete( | ||
| 417 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 418 | __in HRESULT hrStatus | ||
| 419 | ); | ||
| 420 | HRESULT UserExperienceInterpretResult( | ||
| 421 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 422 | __in DWORD dwAllowedResults, | ||
| 423 | __in int nResult | ||
| 424 | ); | ||
| 425 | int UserExperienceCheckExecuteResult( | ||
| 426 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 427 | __in BOOL fRollback, | ||
| 428 | __in DWORD dwAllowedResults, | ||
| 429 | __in int nResult | ||
| 430 | ); | ||
| 431 | HRESULT UserExperienceInterpretExecuteResult( | ||
| 432 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
| 433 | __in BOOL fRollback, | ||
| 434 | __in DWORD dwAllowedResults, | ||
| 435 | __in int nResult | ||
| 436 | ); | ||
| 437 | #if defined(__cplusplus) | ||
| 438 | } | ||
| 439 | #endif | ||
diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp new file mode 100644 index 00000000..ed4abea2 --- /dev/null +++ b/src/engine/variable.cpp | |||
| @@ -0,0 +1,2345 @@ | |||
| 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 | |||
| 5 | |||
| 6 | // structs | ||
| 7 | |||
| 8 | typedef const struct _BUILT_IN_VARIABLE_DECLARATION | ||
| 9 | { | ||
| 10 | LPCWSTR wzVariable; | ||
| 11 | PFN_INITIALIZEVARIABLE pfnInitialize; | ||
| 12 | DWORD_PTR dwpInitializeData; | ||
| 13 | BOOL fPersist; | ||
| 14 | BOOL fOverridable; | ||
| 15 | } BUILT_IN_VARIABLE_DECLARATION; | ||
| 16 | |||
| 17 | |||
| 18 | // constants | ||
| 19 | |||
| 20 | const DWORD GROW_VARIABLE_ARRAY = 3; | ||
| 21 | |||
| 22 | enum OS_INFO_VARIABLE | ||
| 23 | { | ||
| 24 | OS_INFO_VARIABLE_NONE, | ||
| 25 | OS_INFO_VARIABLE_VersionNT, | ||
| 26 | OS_INFO_VARIABLE_VersionNT64, | ||
| 27 | OS_INFO_VARIABLE_ServicePackLevel, | ||
| 28 | OS_INFO_VARIABLE_NTProductType, | ||
| 29 | OS_INFO_VARIABLE_NTSuiteBackOffice, | ||
| 30 | OS_INFO_VARIABLE_NTSuiteDataCenter, | ||
| 31 | OS_INFO_VARIABLE_NTSuiteEnterprise, | ||
| 32 | OS_INFO_VARIABLE_NTSuitePersonal, | ||
| 33 | OS_INFO_VARIABLE_NTSuiteSmallBusiness, | ||
| 34 | OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted, | ||
| 35 | OS_INFO_VARIABLE_NTSuiteWebServer, | ||
| 36 | OS_INFO_VARIABLE_CompatibilityMode, | ||
| 37 | OS_INFO_VARIABLE_TerminalServer, | ||
| 38 | OS_INFO_VARIABLE_ProcessorArchitecture, | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum SET_VARIABLE | ||
| 42 | { | ||
| 43 | SET_VARIABLE_NOT_BUILTIN, | ||
| 44 | SET_VARIABLE_OVERRIDE_BUILTIN, | ||
| 45 | SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS, | ||
| 46 | SET_VARIABLE_ANY, | ||
| 47 | }; | ||
| 48 | |||
| 49 | // internal function declarations | ||
| 50 | |||
| 51 | static HRESULT FormatString( | ||
| 52 | __in BURN_VARIABLES* pVariables, | ||
| 53 | __in_z LPCWSTR wzIn, | ||
| 54 | __out_z_opt LPWSTR* psczOut, | ||
| 55 | __out_opt DWORD* pcchOut, | ||
| 56 | __in BOOL fObfuscateHiddenVariables | ||
| 57 | ); | ||
| 58 | static HRESULT AddBuiltInVariable( | ||
| 59 | __in BURN_VARIABLES* pVariables, | ||
| 60 | __in LPCWSTR wzVariable, | ||
| 61 | __in PFN_INITIALIZEVARIABLE pfnInitialize, | ||
| 62 | __in DWORD_PTR dwpInitializeData, | ||
| 63 | __in BOOL fPersist, | ||
| 64 | __in BOOL fOverridable | ||
| 65 | ); | ||
| 66 | static HRESULT GetVariable( | ||
| 67 | __in BURN_VARIABLES* pVariables, | ||
| 68 | __in_z LPCWSTR wzVariable, | ||
| 69 | __out BURN_VARIABLE** ppVariable | ||
| 70 | ); | ||
| 71 | static HRESULT FindVariableIndexByName( | ||
| 72 | __in BURN_VARIABLES* pVariables, | ||
| 73 | __in_z LPCWSTR wzVariable, | ||
| 74 | __out DWORD* piVariable | ||
| 75 | ); | ||
| 76 | static HRESULT InsertVariable( | ||
| 77 | __in BURN_VARIABLES* pVariables, | ||
| 78 | __in_z LPCWSTR wzVariable, | ||
| 79 | __in DWORD iPosition | ||
| 80 | ); | ||
| 81 | static HRESULT SetVariableValue( | ||
| 82 | __in BURN_VARIABLES* pVariables, | ||
| 83 | __in_z LPCWSTR wzVariable, | ||
| 84 | __in BURN_VARIANT* pVariant, | ||
| 85 | __in BOOL fLiteral, | ||
| 86 | __in SET_VARIABLE setBuiltin, | ||
| 87 | __in BOOL fLog | ||
| 88 | ); | ||
| 89 | static HRESULT InitializeVariableVersionNT( | ||
| 90 | __in DWORD_PTR dwpData, | ||
| 91 | __inout BURN_VARIANT* pValue | ||
| 92 | ); | ||
| 93 | static HRESULT InitializeVariableOsInfo( | ||
| 94 | __in DWORD_PTR dwpData, | ||
| 95 | __inout BURN_VARIANT* pValue | ||
| 96 | ); | ||
| 97 | static HRESULT InitializeVariableSystemInfo( | ||
| 98 | __in DWORD_PTR dwpData, | ||
| 99 | __inout BURN_VARIANT* pValue | ||
| 100 | ); | ||
| 101 | static HRESULT InitializeVariableComputerName( | ||
| 102 | __in DWORD_PTR dwpData, | ||
| 103 | __inout BURN_VARIANT* pValue | ||
| 104 | ); | ||
| 105 | static HRESULT InitializeVariableVersionMsi( | ||
| 106 | __in DWORD_PTR dwpData, | ||
| 107 | __inout BURN_VARIANT* pValue | ||
| 108 | ); | ||
| 109 | static HRESULT InitializeVariableCsidlFolder( | ||
| 110 | __in DWORD_PTR dwpData, | ||
| 111 | __inout BURN_VARIANT* pValue | ||
| 112 | ); | ||
| 113 | static HRESULT InitializeVariableWindowsVolumeFolder( | ||
| 114 | __in DWORD_PTR dwpData, | ||
| 115 | __inout BURN_VARIANT* pValue | ||
| 116 | ); | ||
| 117 | static HRESULT InitializeVariableTempFolder( | ||
| 118 | __in DWORD_PTR dwpData, | ||
| 119 | __inout BURN_VARIANT* pValue | ||
| 120 | ); | ||
| 121 | static HRESULT InitializeVariableSystemFolder( | ||
| 122 | __in DWORD_PTR dwpData, | ||
| 123 | __inout BURN_VARIANT* pValue | ||
| 124 | ); | ||
| 125 | static HRESULT InitializeVariablePrivileged( | ||
| 126 | __in DWORD_PTR dwpData, | ||
| 127 | __inout BURN_VARIANT* pValue | ||
| 128 | ); | ||
| 129 | static HRESULT InitializeVariableRebootPending( | ||
| 130 | __in DWORD_PTR dwpData, | ||
| 131 | __inout BURN_VARIANT* pValue | ||
| 132 | ); | ||
| 133 | static HRESULT InitializeSystemLanguageID( | ||
| 134 | __in DWORD_PTR dwpData, | ||
| 135 | __inout BURN_VARIANT* pValue | ||
| 136 | ); | ||
| 137 | static HRESULT InitializeUserUILanguageID( | ||
| 138 | __in DWORD_PTR dwpData, | ||
| 139 | __inout BURN_VARIANT* pValue | ||
| 140 | ); | ||
| 141 | static HRESULT InitializeUserLanguageID( | ||
| 142 | __in DWORD_PTR dwpData, | ||
| 143 | __inout BURN_VARIANT* pValue | ||
| 144 | ); | ||
| 145 | static HRESULT InitializeVariableString( | ||
| 146 | __in DWORD_PTR dwpData, | ||
| 147 | __inout BURN_VARIANT* pValue | ||
| 148 | ); | ||
| 149 | static HRESULT InitializeVariableNumeric( | ||
| 150 | __in DWORD_PTR dwpData, | ||
| 151 | __inout BURN_VARIANT* pValue | ||
| 152 | ); | ||
| 153 | static HRESULT InitializeVariableRegistryFolder( | ||
| 154 | __in DWORD_PTR dwpData, | ||
| 155 | __inout BURN_VARIANT* pValue | ||
| 156 | ); | ||
| 157 | static HRESULT InitializeVariable6432Folder( | ||
| 158 | __in DWORD_PTR dwpData, | ||
| 159 | __inout BURN_VARIANT* pValue | ||
| 160 | ); | ||
| 161 | static HRESULT InitializeVariableDate( | ||
| 162 | __in DWORD_PTR dwpData, | ||
| 163 | __inout BURN_VARIANT* pValue | ||
| 164 | ); | ||
| 165 | static HRESULT InitializeVariableInstallerName( | ||
| 166 | __in DWORD_PTR dwpData, | ||
| 167 | __inout BURN_VARIANT* pValue | ||
| 168 | ); | ||
| 169 | static HRESULT InitializeVariableInstallerVersion( | ||
| 170 | __in DWORD_PTR dwpData, | ||
| 171 | __inout BURN_VARIANT* pValue | ||
| 172 | ); | ||
| 173 | static HRESULT InitializeVariableVersion( | ||
| 174 | __in DWORD_PTR dwpData, | ||
| 175 | __inout BURN_VARIANT* pValue | ||
| 176 | ); | ||
| 177 | static HRESULT InitializeVariableLogonUser( | ||
| 178 | __in DWORD_PTR dwpData, | ||
| 179 | __inout BURN_VARIANT* pValue | ||
| 180 | ); | ||
| 181 | static HRESULT Get64bitFolderFromRegistry( | ||
| 182 | __in int nFolder, | ||
| 183 | __deref_out_z LPWSTR* psczPath | ||
| 184 | ); | ||
| 185 | |||
| 186 | |||
| 187 | // function definitions | ||
| 188 | |||
| 189 | extern "C" HRESULT VariableInitialize( | ||
| 190 | __in BURN_VARIABLES* pVariables | ||
| 191 | ) | ||
| 192 | { | ||
| 193 | HRESULT hr = S_OK; | ||
| 194 | |||
| 195 | ::InitializeCriticalSection(&pVariables->csAccess); | ||
| 196 | |||
| 197 | const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = { | ||
| 198 | {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS}, | ||
| 199 | {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA}, | ||
| 200 | {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA}, | ||
| 201 | #if defined(_WIN64) | ||
| 202 | {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, | ||
| 203 | {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86}, | ||
| 204 | #else | ||
| 205 | {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON}, | ||
| 206 | {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, | ||
| 207 | #endif | ||
| 208 | {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON}, | ||
| 209 | {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode}, | ||
| 210 | {VARIABLE_DATE, InitializeVariableDate, 0}, | ||
| 211 | {L"ComputerName", InitializeVariableComputerName, 0}, | ||
| 212 | {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP}, | ||
| 213 | {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES}, | ||
| 214 | {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS}, | ||
| 215 | {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0}, | ||
| 216 | {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0}, | ||
| 217 | {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA}, | ||
| 218 | {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0}, | ||
| 219 | {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES}, | ||
| 220 | {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType}, | ||
| 221 | {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice}, | ||
| 222 | {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter}, | ||
| 223 | {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise}, | ||
| 224 | {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal}, | ||
| 225 | {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness}, | ||
| 226 | {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted}, | ||
| 227 | {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer}, | ||
| 228 | {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL}, | ||
| 229 | {L"Privileged", InitializeVariablePrivileged, 0}, | ||
| 230 | {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture}, | ||
| 231 | #if defined(_WIN64) | ||
| 232 | {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, | ||
| 233 | {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86}, | ||
| 234 | #else | ||
| 235 | {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES}, | ||
| 236 | {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, | ||
| 237 | #endif | ||
| 238 | {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, | ||
| 239 | {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, | ||
| 240 | {L"RebootPending", InitializeVariableRebootPending, 0}, | ||
| 241 | {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, | ||
| 242 | {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, | ||
| 243 | {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, | ||
| 244 | {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP}, | ||
| 245 | {L"SystemFolder", InitializeVariableSystemFolder, FALSE}, | ||
| 246 | {L"System64Folder", InitializeVariableSystemFolder, TRUE}, | ||
| 247 | {L"SystemLanguageID", InitializeSystemLanguageID, 0}, | ||
| 248 | {L"TempFolder", InitializeVariableTempFolder, 0}, | ||
| 249 | {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES}, | ||
| 250 | {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer}, | ||
| 251 | {L"UserUILanguageID", InitializeUserUILanguageID, 0}, | ||
| 252 | {L"UserLanguageID", InitializeUserLanguageID, 0}, | ||
| 253 | {L"VersionMsi", InitializeVariableVersionMsi, 0}, | ||
| 254 | {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, | ||
| 255 | {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, | ||
| 256 | {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, | ||
| 257 | {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, | ||
| 258 | {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, | ||
| 259 | {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, | ||
| 260 | {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, | ||
| 261 | {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE}, | ||
| 262 | {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE}, | ||
| 263 | {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE}, | ||
| 264 | {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE}, | ||
| 265 | {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, | ||
| 266 | {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE}, | ||
| 267 | {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, | ||
| 268 | {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, | ||
| 269 | {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, | ||
| 270 | {BURN_BUNDLE_VERSION, InitializeVariableVersion, 0, FALSE, TRUE}, | ||
| 271 | }; | ||
| 272 | |||
| 273 | for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) | ||
| 274 | { | ||
| 275 | BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i]; | ||
| 276 | |||
| 277 | hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable); | ||
| 278 | ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable); | ||
| 279 | } | ||
| 280 | |||
| 281 | LExit: | ||
| 282 | return hr; | ||
| 283 | } | ||
| 284 | |||
| 285 | extern "C" HRESULT VariablesParseFromXml( | ||
| 286 | __in BURN_VARIABLES* pVariables, | ||
| 287 | __in IXMLDOMNode* pixnBundle | ||
| 288 | ) | ||
| 289 | { | ||
| 290 | HRESULT hr = S_OK; | ||
| 291 | IXMLDOMNodeList* pixnNodes = NULL; | ||
| 292 | IXMLDOMNode* pixnNode = NULL; | ||
| 293 | DWORD cNodes = 0; | ||
| 294 | LPWSTR sczId = NULL; | ||
| 295 | LPWSTR scz = NULL; | ||
| 296 | BURN_VARIANT value = { }; | ||
| 297 | BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; | ||
| 298 | BOOL fHidden = FALSE; | ||
| 299 | BOOL fPersisted = FALSE; | ||
| 300 | DWORD iVariable = 0; | ||
| 301 | |||
| 302 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 303 | |||
| 304 | // select variable nodes | ||
| 305 | hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes); | ||
| 306 | ExitOnFailure(hr, "Failed to select variable nodes."); | ||
| 307 | |||
| 308 | // get variable node count | ||
| 309 | hr = pixnNodes->get_length((long*)&cNodes); | ||
| 310 | ExitOnFailure(hr, "Failed to get variable node count."); | ||
| 311 | |||
| 312 | // parse package elements | ||
| 313 | for (DWORD i = 0; i < cNodes; ++i) | ||
| 314 | { | ||
| 315 | hr = XmlNextElement(pixnNodes, &pixnNode, NULL); | ||
| 316 | ExitOnFailure(hr, "Failed to get next node."); | ||
| 317 | |||
| 318 | // @Id | ||
| 319 | hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); | ||
| 320 | ExitOnFailure(hr, "Failed to get @Id."); | ||
| 321 | |||
| 322 | // @Hidden | ||
| 323 | hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden); | ||
| 324 | ExitOnFailure(hr, "Failed to get @Hidden."); | ||
| 325 | |||
| 326 | // @Persisted | ||
| 327 | hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted); | ||
| 328 | ExitOnFailure(hr, "Failed to get @Persisted."); | ||
| 329 | |||
| 330 | // @Value | ||
| 331 | hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); | ||
| 332 | if (E_NOTFOUND != hr) | ||
| 333 | { | ||
| 334 | ExitOnFailure(hr, "Failed to get @Value."); | ||
| 335 | |||
| 336 | hr = BVariantSetString(&value, scz, 0); | ||
| 337 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 338 | |||
| 339 | // @Type | ||
| 340 | hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); | ||
| 341 | ExitOnFailure(hr, "Failed to get @Type."); | ||
| 342 | |||
| 343 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) | ||
| 344 | { | ||
| 345 | if (!fHidden) | ||
| 346 | { | ||
| 347 | LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue); | ||
| 348 | } | ||
| 349 | valueType = BURN_VARIANT_TYPE_NUMERIC; | ||
| 350 | } | ||
| 351 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) | ||
| 352 | { | ||
| 353 | if (!fHidden) | ||
| 354 | { | ||
| 355 | LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue); | ||
| 356 | } | ||
| 357 | valueType = BURN_VARIANT_TYPE_STRING; | ||
| 358 | } | ||
| 359 | else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) | ||
| 360 | { | ||
| 361 | if (!fHidden) | ||
| 362 | { | ||
| 363 | LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue); | ||
| 364 | } | ||
| 365 | valueType = BURN_VARIANT_TYPE_VERSION; | ||
| 366 | } | ||
| 367 | else | ||
| 368 | { | ||
| 369 | hr = E_INVALIDARG; | ||
| 370 | ExitOnFailure(hr, "Invalid value for @Type: %ls", scz); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | else | ||
| 374 | { | ||
| 375 | valueType = BURN_VARIANT_TYPE_NONE; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (fHidden) | ||
| 379 | { | ||
| 380 | LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId); | ||
| 381 | } | ||
| 382 | |||
| 383 | // change value variant to correct type | ||
| 384 | hr = BVariantChangeType(&value, valueType); | ||
| 385 | ExitOnFailure(hr, "Failed to change variant type."); | ||
| 386 | |||
| 387 | // find existing variable | ||
| 388 | hr = FindVariableIndexByName(pVariables, sczId, &iVariable); | ||
| 389 | ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); | ||
| 390 | |||
| 391 | // insert element if not found | ||
| 392 | if (S_FALSE == hr) | ||
| 393 | { | ||
| 394 | hr = InsertVariable(pVariables, sczId, iVariable); | ||
| 395 | ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId); | ||
| 396 | } | ||
| 397 | else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) | ||
| 398 | { | ||
| 399 | hr = E_INVALIDARG; | ||
| 400 | ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId); | ||
| 401 | } | ||
| 402 | pVariables->rgVariables[iVariable].fHidden = fHidden; | ||
| 403 | pVariables->rgVariables[iVariable].fPersisted = fPersisted; | ||
| 404 | |||
| 405 | // update variable value | ||
| 406 | hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value); | ||
| 407 | ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId); | ||
| 408 | |||
| 409 | hr = BVariantSetEncryption(&pVariables->rgVariables[iVariable].Value, fHidden); | ||
| 410 | ExitOnFailure(hr, "Failed to set variant encryption"); | ||
| 411 | |||
| 412 | // prepare next iteration | ||
| 413 | ReleaseNullObject(pixnNode); | ||
| 414 | BVariantUninitialize(&value); | ||
| 415 | ReleaseNullStrSecure(scz); | ||
| 416 | } | ||
| 417 | |||
| 418 | LExit: | ||
| 419 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 420 | |||
| 421 | ReleaseObject(pixnNodes); | ||
| 422 | ReleaseObject(pixnNode); | ||
| 423 | ReleaseStr(scz); | ||
| 424 | ReleaseStr(sczId); | ||
| 425 | BVariantUninitialize(&value); | ||
| 426 | |||
| 427 | return hr; | ||
| 428 | } | ||
| 429 | |||
| 430 | extern "C" void VariablesUninitialize( | ||
| 431 | __in BURN_VARIABLES* pVariables | ||
| 432 | ) | ||
| 433 | { | ||
| 434 | ::DeleteCriticalSection(&pVariables->csAccess); | ||
| 435 | |||
| 436 | if (pVariables->rgVariables) | ||
| 437 | { | ||
| 438 | for (DWORD i = 0; i < pVariables->cVariables; ++i) | ||
| 439 | { | ||
| 440 | BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; | ||
| 441 | if (pVariable) | ||
| 442 | { | ||
| 443 | ReleaseStr(pVariable->sczName); | ||
| 444 | BVariantUninitialize(&pVariable->Value); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | MemFree(pVariables->rgVariables); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | extern "C" void VariablesDump( | ||
| 452 | __in BURN_VARIABLES* pVariables | ||
| 453 | ) | ||
| 454 | { | ||
| 455 | HRESULT hr = S_OK; | ||
| 456 | LPWSTR sczValue = NULL; | ||
| 457 | |||
| 458 | for (DWORD i = 0; i < pVariables->cVariables; ++i) | ||
| 459 | { | ||
| 460 | BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; | ||
| 461 | if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type) | ||
| 462 | { | ||
| 463 | hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName); | ||
| 464 | if (SUCCEEDED(hr)) | ||
| 465 | { | ||
| 466 | if (pVariable->fHidden) | ||
| 467 | { | ||
| 468 | hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL); | ||
| 469 | } | ||
| 470 | else | ||
| 471 | { | ||
| 472 | hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | if (FAILED(hr)) | ||
| 477 | { | ||
| 478 | // already logged; best-effort to dump the rest on our way out the door | ||
| 479 | continue; | ||
| 480 | } | ||
| 481 | |||
| 482 | LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue); | ||
| 483 | |||
| 484 | ReleaseNullStrSecure(sczValue); | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | StrSecureZeroFreeString(sczValue); | ||
| 489 | } | ||
| 490 | |||
| 491 | // The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. | ||
| 492 | extern "C" HRESULT VariableGetNumeric( | ||
| 493 | __in BURN_VARIABLES* pVariables, | ||
| 494 | __in_z LPCWSTR wzVariable, | ||
| 495 | __out LONGLONG* pllValue | ||
| 496 | ) | ||
| 497 | { | ||
| 498 | HRESULT hr = S_OK; | ||
| 499 | BURN_VARIABLE* pVariable = NULL; | ||
| 500 | |||
| 501 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 502 | |||
| 503 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 504 | if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) | ||
| 505 | { | ||
| 506 | ExitFunction1(hr = E_NOTFOUND); | ||
| 507 | } | ||
| 508 | else if (E_NOTFOUND == hr) | ||
| 509 | { | ||
| 510 | ExitFunction(); | ||
| 511 | } | ||
| 512 | ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); | ||
| 513 | |||
| 514 | hr = BVariantGetNumeric(&pVariable->Value, pllValue); | ||
| 515 | ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable); | ||
| 516 | |||
| 517 | LExit: | ||
| 518 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 519 | |||
| 520 | return hr; | ||
| 521 | } | ||
| 522 | |||
| 523 | // The contents of psczValue may be sensitive, if variable is hidden should keep encrypted and SecureZeroFree. | ||
| 524 | extern "C" HRESULT VariableGetString( | ||
| 525 | __in BURN_VARIABLES* pVariables, | ||
| 526 | __in_z LPCWSTR wzVariable, | ||
| 527 | __out_z LPWSTR* psczValue | ||
| 528 | ) | ||
| 529 | { | ||
| 530 | HRESULT hr = S_OK; | ||
| 531 | BURN_VARIABLE* pVariable = NULL; | ||
| 532 | |||
| 533 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 534 | |||
| 535 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 536 | if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) | ||
| 537 | { | ||
| 538 | ExitFunction1(hr = E_NOTFOUND); | ||
| 539 | } | ||
| 540 | else if (E_NOTFOUND == hr) | ||
| 541 | { | ||
| 542 | ExitFunction(); | ||
| 543 | } | ||
| 544 | ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); | ||
| 545 | |||
| 546 | hr = BVariantGetString(&pVariable->Value, psczValue); | ||
| 547 | ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); | ||
| 548 | |||
| 549 | LExit: | ||
| 550 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 551 | |||
| 552 | return hr; | ||
| 553 | } | ||
| 554 | |||
| 555 | // The contents of pqwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory. | ||
| 556 | extern "C" HRESULT VariableGetVersion( | ||
| 557 | __in BURN_VARIABLES* pVariables, | ||
| 558 | __in_z LPCWSTR wzVariable, | ||
| 559 | __in DWORD64* pqwValue | ||
| 560 | ) | ||
| 561 | { | ||
| 562 | HRESULT hr = S_OK; | ||
| 563 | BURN_VARIABLE* pVariable = NULL; | ||
| 564 | |||
| 565 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 566 | |||
| 567 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 568 | if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) | ||
| 569 | { | ||
| 570 | ExitFunction1(hr = E_NOTFOUND); | ||
| 571 | } | ||
| 572 | else if (E_NOTFOUND == hr) | ||
| 573 | { | ||
| 574 | ExitFunction(); | ||
| 575 | } | ||
| 576 | ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); | ||
| 577 | |||
| 578 | hr = BVariantGetVersion(&pVariable->Value, pqwValue); | ||
| 579 | ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); | ||
| 580 | |||
| 581 | LExit: | ||
| 582 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 583 | |||
| 584 | return hr; | ||
| 585 | } | ||
| 586 | |||
| 587 | extern "C" HRESULT VariableGetVariant( | ||
| 588 | __in BURN_VARIABLES* pVariables, | ||
| 589 | __in_z LPCWSTR wzVariable, | ||
| 590 | __in BURN_VARIANT* pValue | ||
| 591 | ) | ||
| 592 | { | ||
| 593 | HRESULT hr = S_OK; | ||
| 594 | BURN_VARIABLE* pVariable = NULL; | ||
| 595 | |||
| 596 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 597 | |||
| 598 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 599 | if (E_NOTFOUND == hr) | ||
| 600 | { | ||
| 601 | ExitFunction(); | ||
| 602 | } | ||
| 603 | ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); | ||
| 604 | |||
| 605 | hr = BVariantCopy(&pVariable->Value, pValue); | ||
| 606 | ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable); | ||
| 607 | |||
| 608 | LExit: | ||
| 609 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 610 | |||
| 611 | return hr; | ||
| 612 | } | ||
| 613 | |||
| 614 | // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. | ||
| 615 | extern "C" HRESULT VariableGetFormatted( | ||
| 616 | __in BURN_VARIABLES* pVariables, | ||
| 617 | __in_z LPCWSTR wzVariable, | ||
| 618 | __out_z LPWSTR* psczValue | ||
| 619 | ) | ||
| 620 | { | ||
| 621 | HRESULT hr = S_OK; | ||
| 622 | BURN_VARIABLE* pVariable = NULL; | ||
| 623 | LPWSTR scz = NULL; | ||
| 624 | |||
| 625 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 626 | |||
| 627 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 628 | if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) | ||
| 629 | { | ||
| 630 | ExitFunction1(hr = E_NOTFOUND); | ||
| 631 | } | ||
| 632 | else if (E_NOTFOUND == hr) | ||
| 633 | { | ||
| 634 | ExitFunction(); | ||
| 635 | } | ||
| 636 | ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); | ||
| 637 | |||
| 638 | // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables. | ||
| 639 | if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type && | ||
| 640 | BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType && | ||
| 641 | !pVariable->fLiteral) | ||
| 642 | { | ||
| 643 | hr = BVariantGetString(&pVariable->Value, &scz); | ||
| 644 | ExitOnFailure(hr, "Failed to get unformatted string."); | ||
| 645 | |||
| 646 | hr = VariableFormatString(pVariables, scz, psczValue, NULL); | ||
| 647 | ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); | ||
| 648 | } | ||
| 649 | else | ||
| 650 | { | ||
| 651 | hr = BVariantGetString(&pVariable->Value, psczValue); | ||
| 652 | ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); | ||
| 653 | } | ||
| 654 | |||
| 655 | LExit: | ||
| 656 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 657 | StrSecureZeroFreeString(scz); | ||
| 658 | |||
| 659 | return hr; | ||
| 660 | } | ||
| 661 | |||
| 662 | extern "C" HRESULT VariableSetNumeric( | ||
| 663 | __in BURN_VARIABLES* pVariables, | ||
| 664 | __in_z LPCWSTR wzVariable, | ||
| 665 | __in LONGLONG llValue, | ||
| 666 | __in BOOL fOverwriteBuiltIn | ||
| 667 | ) | ||
| 668 | { | ||
| 669 | BURN_VARIANT variant = { }; | ||
| 670 | |||
| 671 | // We're not going to encrypt this value, so can access the value directly. | ||
| 672 | variant.llValue = llValue; | ||
| 673 | variant.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 674 | |||
| 675 | return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); | ||
| 676 | } | ||
| 677 | |||
| 678 | extern "C" HRESULT VariableSetLiteralString( | ||
| 679 | __in BURN_VARIABLES* pVariables, | ||
| 680 | __in_z LPCWSTR wzVariable, | ||
| 681 | __in_z_opt LPCWSTR wzValue, | ||
| 682 | __in BOOL fOverwriteBuiltIn | ||
| 683 | ) | ||
| 684 | { | ||
| 685 | BURN_VARIANT variant = { }; | ||
| 686 | |||
| 687 | // We're not going to encrypt this value, so can access the value directly. | ||
| 688 | variant.sczValue = (LPWSTR)wzValue; | ||
| 689 | variant.Type = BURN_VARIANT_TYPE_STRING; | ||
| 690 | |||
| 691 | return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); | ||
| 692 | } | ||
| 693 | |||
| 694 | extern "C" HRESULT VariableSetString( | ||
| 695 | __in BURN_VARIABLES* pVariables, | ||
| 696 | __in_z LPCWSTR wzVariable, | ||
| 697 | __in_z_opt LPCWSTR wzValue, | ||
| 698 | __in BOOL fOverwriteBuiltIn | ||
| 699 | ) | ||
| 700 | { | ||
| 701 | BURN_VARIANT variant = { }; | ||
| 702 | |||
| 703 | // We're not going to encrypt this value, so can access the value directly. | ||
| 704 | variant.sczValue = (LPWSTR)wzValue; | ||
| 705 | variant.Type = BURN_VARIANT_TYPE_STRING; | ||
| 706 | |||
| 707 | return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); | ||
| 708 | } | ||
| 709 | |||
| 710 | extern "C" HRESULT VariableSetVersion( | ||
| 711 | __in BURN_VARIABLES* pVariables, | ||
| 712 | __in_z LPCWSTR wzVariable, | ||
| 713 | __in DWORD64 qwValue, | ||
| 714 | __in BOOL fOverwriteBuiltIn | ||
| 715 | ) | ||
| 716 | { | ||
| 717 | BURN_VARIANT variant = { }; | ||
| 718 | |||
| 719 | // We're not going to encrypt this value, so can access the value directly. | ||
| 720 | variant.qwValue = qwValue; | ||
| 721 | variant.Type = BURN_VARIANT_TYPE_VERSION; | ||
| 722 | |||
| 723 | return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); | ||
| 724 | } | ||
| 725 | |||
| 726 | extern "C" HRESULT VariableSetLiteralVariant( | ||
| 727 | __in BURN_VARIABLES* pVariables, | ||
| 728 | __in_z LPCWSTR wzVariable, | ||
| 729 | __in BURN_VARIANT* pVariant | ||
| 730 | ) | ||
| 731 | { | ||
| 732 | return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE); | ||
| 733 | } | ||
| 734 | |||
| 735 | // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree | ||
| 736 | extern "C" HRESULT VariableFormatString( | ||
| 737 | __in BURN_VARIABLES* pVariables, | ||
| 738 | __in_z LPCWSTR wzIn, | ||
| 739 | __out_z_opt LPWSTR* psczOut, | ||
| 740 | __out_opt DWORD* pcchOut | ||
| 741 | ) | ||
| 742 | { | ||
| 743 | return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE); | ||
| 744 | } | ||
| 745 | |||
| 746 | extern "C" HRESULT VariableFormatStringObfuscated( | ||
| 747 | __in BURN_VARIABLES* pVariables, | ||
| 748 | __in_z LPCWSTR wzIn, | ||
| 749 | __out_z_opt LPWSTR* psczOut, | ||
| 750 | __out_opt DWORD* pcchOut | ||
| 751 | ) | ||
| 752 | { | ||
| 753 | return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE); | ||
| 754 | } | ||
| 755 | |||
| 756 | extern "C" HRESULT VariableEscapeString( | ||
| 757 | __in_z LPCWSTR wzIn, | ||
| 758 | __out_z LPWSTR* psczOut | ||
| 759 | ) | ||
| 760 | { | ||
| 761 | HRESULT hr = S_OK; | ||
| 762 | LPCWSTR wzRead = NULL; | ||
| 763 | LPWSTR pwzEscaped = NULL; | ||
| 764 | LPWSTR pwz = NULL; | ||
| 765 | SIZE_T i = 0; | ||
| 766 | |||
| 767 | // allocate buffer for escaped string | ||
| 768 | hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1); | ||
| 769 | ExitOnFailure(hr, "Failed to allocate buffer for escaped string."); | ||
| 770 | |||
| 771 | // read through string and move characters, inserting escapes as needed | ||
| 772 | wzRead = wzIn; | ||
| 773 | for (;;) | ||
| 774 | { | ||
| 775 | // find next character needing escaping | ||
| 776 | i = wcscspn(wzRead, L"[]{}"); | ||
| 777 | |||
| 778 | // copy skipped characters | ||
| 779 | if (0 < i) | ||
| 780 | { | ||
| 781 | hr = StrAllocConcat(&pwzEscaped, wzRead, i); | ||
| 782 | ExitOnFailure(hr, "Failed to append characters."); | ||
| 783 | } | ||
| 784 | |||
| 785 | if (L'\0' == wzRead[i]) | ||
| 786 | { | ||
| 787 | break; // end reached | ||
| 788 | } | ||
| 789 | |||
| 790 | // escape character | ||
| 791 | hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]); | ||
| 792 | ExitOnFailure(hr, "Failed to format escape sequence."); | ||
| 793 | |||
| 794 | hr = StrAllocConcat(&pwzEscaped, pwz, 0); | ||
| 795 | ExitOnFailure(hr, "Failed to append escape sequence."); | ||
| 796 | |||
| 797 | // update read pointer | ||
| 798 | wzRead += i + 1; | ||
| 799 | } | ||
| 800 | |||
| 801 | // return value | ||
| 802 | hr = StrAllocString(psczOut, pwzEscaped, 0); | ||
| 803 | ExitOnFailure(hr, "Failed to copy string."); | ||
| 804 | |||
| 805 | LExit: | ||
| 806 | ReleaseStr(pwzEscaped); | ||
| 807 | ReleaseStr(pwz); | ||
| 808 | return hr; | ||
| 809 | } | ||
| 810 | |||
| 811 | extern "C" HRESULT VariableSerialize( | ||
| 812 | __in BURN_VARIABLES* pVariables, | ||
| 813 | __in BOOL fPersisting, | ||
| 814 | __inout BYTE** ppbBuffer, | ||
| 815 | __inout SIZE_T* piBuffer | ||
| 816 | ) | ||
| 817 | { | ||
| 818 | HRESULT hr = S_OK; | ||
| 819 | BOOL fIncluded = FALSE; | ||
| 820 | LONGLONG ll = 0; | ||
| 821 | LPWSTR scz = NULL; | ||
| 822 | DWORD64 qw = 0; | ||
| 823 | |||
| 824 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 825 | |||
| 826 | // Write variable count. | ||
| 827 | hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables); | ||
| 828 | ExitOnFailure(hr, "Failed to write variable count."); | ||
| 829 | |||
| 830 | // Write variables. | ||
| 831 | for (DWORD i = 0; i < pVariables->cVariables; ++i) | ||
| 832 | { | ||
| 833 | BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; | ||
| 834 | |||
| 835 | // If we aren't persisting, include only variables that aren't rejected by the elevated process. | ||
| 836 | // If we are persisting, include only variables that should be persisted. | ||
| 837 | fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) || | ||
| 838 | (fPersisting && pVariable->fPersisted); | ||
| 839 | |||
| 840 | // Write included flag. | ||
| 841 | hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded); | ||
| 842 | ExitOnFailure(hr, "Failed to write included flag."); | ||
| 843 | |||
| 844 | if (!fIncluded) | ||
| 845 | { | ||
| 846 | continue; | ||
| 847 | } | ||
| 848 | |||
| 849 | // Write variable name. | ||
| 850 | hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName); | ||
| 851 | ExitOnFailure(hr, "Failed to write variable name."); | ||
| 852 | |||
| 853 | // Write variable value type. | ||
| 854 | hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type); | ||
| 855 | ExitOnFailure(hr, "Failed to write variable value type."); | ||
| 856 | |||
| 857 | // Write variable value. | ||
| 858 | switch (pVariable->Value.Type) | ||
| 859 | { | ||
| 860 | case BURN_VARIANT_TYPE_NONE: | ||
| 861 | break; | ||
| 862 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 863 | hr = BVariantGetNumeric(&pVariable->Value, &ll); | ||
| 864 | ExitOnFailure(hr, "Failed to get numeric."); | ||
| 865 | |||
| 866 | hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast<DWORD64>(ll)); | ||
| 867 | ExitOnFailure(hr, "Failed to write variable value as number."); | ||
| 868 | |||
| 869 | SecureZeroMemory(&ll, sizeof(ll)); | ||
| 870 | break; | ||
| 871 | case BURN_VARIANT_TYPE_VERSION: | ||
| 872 | hr = BVariantGetVersion(&pVariable->Value, &qw); | ||
| 873 | ExitOnFailure(hr, "Failed to get version."); | ||
| 874 | |||
| 875 | hr = BuffWriteNumber64(ppbBuffer, piBuffer, qw); | ||
| 876 | ExitOnFailure(hr, "Failed to write variable value as number."); | ||
| 877 | |||
| 878 | SecureZeroMemory(&qw, sizeof(qw)); | ||
| 879 | break; | ||
| 880 | case BURN_VARIANT_TYPE_STRING: | ||
| 881 | hr = BVariantGetString(&pVariable->Value, &scz); | ||
| 882 | ExitOnFailure(hr, "Failed to get string."); | ||
| 883 | |||
| 884 | hr = BuffWriteString(ppbBuffer, piBuffer, scz); | ||
| 885 | ExitOnFailure(hr, "Failed to write variable value as string."); | ||
| 886 | |||
| 887 | ReleaseNullStrSecure(scz); | ||
| 888 | break; | ||
| 889 | default: | ||
| 890 | hr = E_INVALIDARG; | ||
| 891 | ExitOnFailure(hr, "Unsupported variable type."); | ||
| 892 | } | ||
| 893 | |||
| 894 | // Write literal flag. | ||
| 895 | hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral); | ||
| 896 | ExitOnFailure(hr, "Failed to write literal flag."); | ||
| 897 | } | ||
| 898 | |||
| 899 | LExit: | ||
| 900 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 901 | SecureZeroMemory(&ll, sizeof(ll)); | ||
| 902 | SecureZeroMemory(&qw, sizeof(qw)); | ||
| 903 | StrSecureZeroFreeString(scz); | ||
| 904 | |||
| 905 | return hr; | ||
| 906 | } | ||
| 907 | |||
| 908 | extern "C" HRESULT VariableDeserialize( | ||
| 909 | __in BURN_VARIABLES* pVariables, | ||
| 910 | __in BOOL fWasPersisted, | ||
| 911 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 912 | __in SIZE_T cbBuffer, | ||
| 913 | __inout SIZE_T* piBuffer | ||
| 914 | ) | ||
| 915 | { | ||
| 916 | HRESULT hr = S_OK; | ||
| 917 | DWORD cVariables = 0; | ||
| 918 | LPWSTR sczName = NULL; | ||
| 919 | BOOL fIncluded = FALSE; | ||
| 920 | BOOL fLiteral = FALSE; | ||
| 921 | BURN_VARIANT value = { }; | ||
| 922 | LPWSTR scz = NULL; | ||
| 923 | DWORD64 qw = 0; | ||
| 924 | |||
| 925 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 926 | |||
| 927 | // Read variable count. | ||
| 928 | hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables); | ||
| 929 | ExitOnFailure(hr, "Failed to read variable count."); | ||
| 930 | |||
| 931 | // Read variables. | ||
| 932 | for (DWORD i = 0; i < cVariables; ++i) | ||
| 933 | { | ||
| 934 | // Read variable included flag. | ||
| 935 | hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded); | ||
| 936 | ExitOnFailure(hr, "Failed to read variable included flag."); | ||
| 937 | |||
| 938 | if (!fIncluded) | ||
| 939 | { | ||
| 940 | continue; // if variable is not included, skip. | ||
| 941 | } | ||
| 942 | |||
| 943 | // Read variable name. | ||
| 944 | hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName); | ||
| 945 | ExitOnFailure(hr, "Failed to read variable name."); | ||
| 946 | |||
| 947 | // Read variable value type. | ||
| 948 | hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type); | ||
| 949 | ExitOnFailure(hr, "Failed to read variable value type."); | ||
| 950 | |||
| 951 | // Read variable value. | ||
| 952 | switch (value.Type) | ||
| 953 | { | ||
| 954 | case BURN_VARIANT_TYPE_NONE: | ||
| 955 | break; | ||
| 956 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 957 | hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); | ||
| 958 | ExitOnFailure(hr, "Failed to read variable value as number."); | ||
| 959 | |||
| 960 | hr = BVariantSetNumeric(&value, static_cast<LONGLONG>(qw)); | ||
| 961 | ExitOnFailure(hr, "Failed to set variable value."); | ||
| 962 | |||
| 963 | SecureZeroMemory(&qw, sizeof(qw)); | ||
| 964 | break; | ||
| 965 | case BURN_VARIANT_TYPE_VERSION: | ||
| 966 | hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); | ||
| 967 | ExitOnFailure(hr, "Failed to read variable value as number."); | ||
| 968 | |||
| 969 | hr = BVariantSetVersion(&value, qw); | ||
| 970 | ExitOnFailure(hr, "Failed to set variable value."); | ||
| 971 | |||
| 972 | SecureZeroMemory(&qw, sizeof(qw)); | ||
| 973 | break; | ||
| 974 | case BURN_VARIANT_TYPE_STRING: | ||
| 975 | hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); | ||
| 976 | ExitOnFailure(hr, "Failed to read variable value as string."); | ||
| 977 | |||
| 978 | hr = BVariantSetString(&value, scz, NULL); | ||
| 979 | ExitOnFailure(hr, "Failed to set variable value."); | ||
| 980 | |||
| 981 | ReleaseNullStrSecure(scz); | ||
| 982 | break; | ||
| 983 | default: | ||
| 984 | hr = E_INVALIDARG; | ||
| 985 | ExitOnFailure(hr, "Unsupported variable type."); | ||
| 986 | } | ||
| 987 | |||
| 988 | // Read variable literal flag. | ||
| 989 | hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral); | ||
| 990 | ExitOnFailure(hr, "Failed to read variable literal flag."); | ||
| 991 | |||
| 992 | // Set variable. | ||
| 993 | hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); | ||
| 994 | ExitOnFailure(hr, "Failed to set variable."); | ||
| 995 | |||
| 996 | // Clean up. | ||
| 997 | BVariantUninitialize(&value); | ||
| 998 | } | ||
| 999 | |||
| 1000 | LExit: | ||
| 1001 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 1002 | |||
| 1003 | ReleaseStr(sczName); | ||
| 1004 | BVariantUninitialize(&value); | ||
| 1005 | SecureZeroMemory(&qw, sizeof(qw)); | ||
| 1006 | StrSecureZeroFreeString(scz); | ||
| 1007 | |||
| 1008 | return hr; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | extern "C" HRESULT VariableStrAlloc( | ||
| 1012 | __in BOOL fZeroOnRealloc, | ||
| 1013 | __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, | ||
| 1014 | __in DWORD_PTR cch | ||
| 1015 | ) | ||
| 1016 | { | ||
| 1017 | HRESULT hr = S_OK; | ||
| 1018 | |||
| 1019 | if (fZeroOnRealloc) | ||
| 1020 | { | ||
| 1021 | hr = StrAllocSecure(ppwz, cch); | ||
| 1022 | } | ||
| 1023 | else | ||
| 1024 | { | ||
| 1025 | hr = StrAlloc(ppwz, cch); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | return hr; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | extern "C" HRESULT VariableStrAllocString( | ||
| 1032 | __in BOOL fZeroOnRealloc, | ||
| 1033 | __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, | ||
| 1034 | __in_z LPCWSTR wzSource, | ||
| 1035 | __in DWORD_PTR cchSource | ||
| 1036 | ) | ||
| 1037 | { | ||
| 1038 | HRESULT hr = S_OK; | ||
| 1039 | |||
| 1040 | if (fZeroOnRealloc) | ||
| 1041 | { | ||
| 1042 | hr = StrAllocStringSecure(ppwz, wzSource, cchSource); | ||
| 1043 | } | ||
| 1044 | else | ||
| 1045 | { | ||
| 1046 | hr = StrAllocString(ppwz, wzSource, cchSource); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | return hr; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | extern "C" HRESULT VariableStrAllocConcat( | ||
| 1053 | __in BOOL fZeroOnRealloc, | ||
| 1054 | __deref_out_z LPWSTR* ppwz, | ||
| 1055 | __in_z LPCWSTR wzSource, | ||
| 1056 | __in DWORD_PTR cchSource | ||
| 1057 | ) | ||
| 1058 | { | ||
| 1059 | HRESULT hr = S_OK; | ||
| 1060 | |||
| 1061 | if (fZeroOnRealloc) | ||
| 1062 | { | ||
| 1063 | hr = StrAllocConcatSecure(ppwz, wzSource, cchSource); | ||
| 1064 | } | ||
| 1065 | else | ||
| 1066 | { | ||
| 1067 | hr = StrAllocConcat(ppwz, wzSource, cchSource); | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | return hr; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | extern "C" HRESULT __cdecl VariableStrAllocFormatted( | ||
| 1074 | __in BOOL fZeroOnRealloc, | ||
| 1075 | __deref_out_z LPWSTR* ppwz, | ||
| 1076 | __in __format_string LPCWSTR wzFormat, | ||
| 1077 | ... | ||
| 1078 | ) | ||
| 1079 | { | ||
| 1080 | HRESULT hr = S_OK; | ||
| 1081 | va_list args; | ||
| 1082 | |||
| 1083 | va_start(args, wzFormat); | ||
| 1084 | if (fZeroOnRealloc) | ||
| 1085 | { | ||
| 1086 | hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); | ||
| 1087 | } | ||
| 1088 | else | ||
| 1089 | { | ||
| 1090 | hr = StrAllocFormattedArgs(ppwz, wzFormat, args); | ||
| 1091 | } | ||
| 1092 | va_end(args); | ||
| 1093 | |||
| 1094 | return hr; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | extern "C" HRESULT VariableIsHidden( | ||
| 1098 | __in BURN_VARIABLES* pVariables, | ||
| 1099 | __in_z LPCWSTR wzVariable, | ||
| 1100 | __out BOOL* pfHidden | ||
| 1101 | ) | ||
| 1102 | { | ||
| 1103 | HRESULT hr = S_OK; | ||
| 1104 | BURN_VARIABLE* pVariable = NULL; | ||
| 1105 | |||
| 1106 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 1107 | |||
| 1108 | hr = GetVariable(pVariables, wzVariable, &pVariable); | ||
| 1109 | if (E_NOTFOUND == hr) | ||
| 1110 | { | ||
| 1111 | // A missing variable does not need its data hidden. | ||
| 1112 | *pfHidden = FALSE; | ||
| 1113 | |||
| 1114 | ExitFunction1(hr = S_OK); | ||
| 1115 | } | ||
| 1116 | ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable); | ||
| 1117 | |||
| 1118 | *pfHidden = pVariable->fHidden; | ||
| 1119 | |||
| 1120 | LExit: | ||
| 1121 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 1122 | |||
| 1123 | return hr; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | |||
| 1127 | // internal function definitions | ||
| 1128 | |||
| 1129 | // The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree. | ||
| 1130 | static HRESULT FormatString( | ||
| 1131 | __in BURN_VARIABLES* pVariables, | ||
| 1132 | __in_z LPCWSTR wzIn, | ||
| 1133 | __out_z_opt LPWSTR* psczOut, | ||
| 1134 | __out_opt DWORD* pcchOut, | ||
| 1135 | __in BOOL fObfuscateHiddenVariables | ||
| 1136 | ) | ||
| 1137 | { | ||
| 1138 | HRESULT hr = S_OK; | ||
| 1139 | DWORD er = ERROR_SUCCESS; | ||
| 1140 | LPWSTR sczUnformatted = NULL; | ||
| 1141 | LPWSTR sczFormat = NULL; | ||
| 1142 | LPCWSTR wzRead = NULL; | ||
| 1143 | LPCWSTR wzOpen = NULL; | ||
| 1144 | LPCWSTR wzClose = NULL; | ||
| 1145 | LPWSTR scz = NULL; | ||
| 1146 | LPWSTR* rgVariables = NULL; | ||
| 1147 | DWORD cVariables = 0; | ||
| 1148 | DWORD cch = 0; | ||
| 1149 | BOOL fHidden = FALSE; | ||
| 1150 | MSIHANDLE hRecord = NULL; | ||
| 1151 | |||
| 1152 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 1153 | |||
| 1154 | // allocate buffer for format string | ||
| 1155 | hr = StrAlloc(&sczFormat, lstrlenW(wzIn) + 1); | ||
| 1156 | ExitOnFailure(hr, "Failed to allocate buffer for format string."); | ||
| 1157 | |||
| 1158 | // read out variables from the unformatted string and build a format string | ||
| 1159 | wzRead = wzIn; | ||
| 1160 | for (;;) | ||
| 1161 | { | ||
| 1162 | // scan for opening '[' | ||
| 1163 | wzOpen = wcschr(wzRead, L'['); | ||
| 1164 | if (!wzOpen) | ||
| 1165 | { | ||
| 1166 | // end reached, append the remainder of the string and end loop | ||
| 1167 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); | ||
| 1168 | ExitOnFailure(hr, "Failed to append string."); | ||
| 1169 | break; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | // scan for closing ']' | ||
| 1173 | wzClose = wcschr(wzOpen + 1, L']'); | ||
| 1174 | if (!wzClose) | ||
| 1175 | { | ||
| 1176 | // end reached, treat unterminated expander as literal | ||
| 1177 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); | ||
| 1178 | ExitOnFailure(hr, "Failed to append string."); | ||
| 1179 | break; | ||
| 1180 | } | ||
| 1181 | cch = wzClose - wzOpen - 1; | ||
| 1182 | |||
| 1183 | if (0 == cch) | ||
| 1184 | { | ||
| 1185 | // blank, copy all text including the terminator | ||
| 1186 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1); | ||
| 1187 | ExitOnFailure(hr, "Failed to append string."); | ||
| 1188 | } | ||
| 1189 | else | ||
| 1190 | { | ||
| 1191 | // append text preceding expander | ||
| 1192 | if (wzOpen > wzRead) | ||
| 1193 | { | ||
| 1194 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead)); | ||
| 1195 | ExitOnFailure(hr, "Failed to append string."); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | // get variable name | ||
| 1199 | hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch); | ||
| 1200 | ExitOnFailure(hr, "Failed to get variable name."); | ||
| 1201 | |||
| 1202 | // allocate space in variable array | ||
| 1203 | if (rgVariables) | ||
| 1204 | { | ||
| 1205 | LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE); | ||
| 1206 | ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array."); | ||
| 1207 | rgVariables = (LPWSTR*)pv; | ||
| 1208 | } | ||
| 1209 | else | ||
| 1210 | { | ||
| 1211 | rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE); | ||
| 1212 | ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array."); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | // set variable value | ||
| 1216 | if (2 <= cch && L'\\' == wzOpen[1]) | ||
| 1217 | { | ||
| 1218 | // escape sequence, copy character | ||
| 1219 | hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1); | ||
| 1220 | } | ||
| 1221 | else | ||
| 1222 | { | ||
| 1223 | if (fObfuscateHiddenVariables) | ||
| 1224 | { | ||
| 1225 | hr = VariableIsHidden(pVariables, scz, &fHidden); | ||
| 1226 | ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | if (fHidden) | ||
| 1230 | { | ||
| 1231 | hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); | ||
| 1232 | } | ||
| 1233 | else | ||
| 1234 | { | ||
| 1235 | // get formatted variable value | ||
| 1236 | hr = VariableGetFormatted(pVariables, scz, &rgVariables[cVariables]); | ||
| 1237 | if (E_NOTFOUND == hr) // variable not found | ||
| 1238 | { | ||
| 1239 | hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | ExitOnFailure(hr, "Failed to set variable value."); | ||
| 1244 | ++cVariables; | ||
| 1245 | |||
| 1246 | // append placeholder to format string | ||
| 1247 | hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables); | ||
| 1248 | ExitOnFailure(hr, "Failed to format placeholder string."); | ||
| 1249 | |||
| 1250 | hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0); | ||
| 1251 | ExitOnFailure(hr, "Failed to append placeholder."); | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | // update read pointer | ||
| 1255 | wzRead = wzClose + 1; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | // create record | ||
| 1259 | hRecord = ::MsiCreateRecord(cVariables); | ||
| 1260 | ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record."); | ||
| 1261 | |||
| 1262 | // set format string | ||
| 1263 | er = ::MsiRecordSetStringW(hRecord, 0, sczFormat); | ||
| 1264 | ExitOnWin32Error(er, hr, "Failed to set record format string."); | ||
| 1265 | |||
| 1266 | // copy record fields | ||
| 1267 | for (DWORD i = 0; i < cVariables; ++i) | ||
| 1268 | { | ||
| 1269 | if (*rgVariables[i]) // not setting if blank | ||
| 1270 | { | ||
| 1271 | er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]); | ||
| 1272 | ExitOnWin32Error(er, hr, "Failed to set record string."); | ||
| 1273 | } | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | // get formatted character count | ||
| 1277 | cch = 0; | ||
| 1278 | #pragma prefast(push) | ||
| 1279 | #pragma prefast(disable:6298) | ||
| 1280 | er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch); | ||
| 1281 | #pragma prefast(pop) | ||
| 1282 | if (ERROR_MORE_DATA != er) | ||
| 1283 | { | ||
| 1284 | ExitOnWin32Error(er, hr, "Failed to get formatted length."); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | // return formatted string | ||
| 1288 | if (psczOut) | ||
| 1289 | { | ||
| 1290 | hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch); | ||
| 1291 | ExitOnFailure(hr, "Failed to allocate string."); | ||
| 1292 | |||
| 1293 | er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch); | ||
| 1294 | ExitOnWin32Error(er, hr, "Failed to format record."); | ||
| 1295 | |||
| 1296 | hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0); | ||
| 1297 | ExitOnFailure(hr, "Failed to copy string."); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | // return character count | ||
| 1301 | if (pcchOut) | ||
| 1302 | { | ||
| 1303 | *pcchOut = cch; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | LExit: | ||
| 1307 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 1308 | |||
| 1309 | if (rgVariables) | ||
| 1310 | { | ||
| 1311 | for (DWORD i = 0; i < cVariables; ++i) | ||
| 1312 | { | ||
| 1313 | if (fObfuscateHiddenVariables) | ||
| 1314 | { | ||
| 1315 | ReleaseStr(rgVariables[i]); | ||
| 1316 | } | ||
| 1317 | else | ||
| 1318 | { | ||
| 1319 | StrSecureZeroFreeString(rgVariables[i]); | ||
| 1320 | } | ||
| 1321 | } | ||
| 1322 | MemFree(rgVariables); | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | if (hRecord) | ||
| 1326 | { | ||
| 1327 | ::MsiCloseHandle(hRecord); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | if (fObfuscateHiddenVariables) | ||
| 1331 | { | ||
| 1332 | ReleaseStr(sczUnformatted); | ||
| 1333 | ReleaseStr(sczFormat); | ||
| 1334 | ReleaseStr(scz); | ||
| 1335 | } | ||
| 1336 | else | ||
| 1337 | { | ||
| 1338 | StrSecureZeroFreeString(sczUnformatted); | ||
| 1339 | StrSecureZeroFreeString(sczFormat); | ||
| 1340 | StrSecureZeroFreeString(scz); | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | return hr; | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | static HRESULT AddBuiltInVariable( | ||
| 1347 | __in BURN_VARIABLES* pVariables, | ||
| 1348 | __in LPCWSTR wzVariable, | ||
| 1349 | __in PFN_INITIALIZEVARIABLE pfnInitialize, | ||
| 1350 | __in DWORD_PTR dwpInitializeData, | ||
| 1351 | __in BOOL fPersist, | ||
| 1352 | __in BOOL fOverridable | ||
| 1353 | ) | ||
| 1354 | { | ||
| 1355 | HRESULT hr = S_OK; | ||
| 1356 | DWORD iVariable = 0; | ||
| 1357 | BURN_VARIABLE* pVariable = NULL; | ||
| 1358 | |||
| 1359 | hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); | ||
| 1360 | ExitOnFailure(hr, "Failed to find variable value."); | ||
| 1361 | |||
| 1362 | // insert element if not found | ||
| 1363 | if (S_FALSE == hr) | ||
| 1364 | { | ||
| 1365 | hr = InsertVariable(pVariables, wzVariable, iVariable); | ||
| 1366 | ExitOnFailure(hr, "Failed to insert variable."); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | // set variable values | ||
| 1370 | pVariable = &pVariables->rgVariables[iVariable]; | ||
| 1371 | pVariable->fPersisted = fPersist; | ||
| 1372 | pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN; | ||
| 1373 | pVariable->pfnInitialize = pfnInitialize; | ||
| 1374 | pVariable->dwpInitializeData = dwpInitializeData; | ||
| 1375 | |||
| 1376 | LExit: | ||
| 1377 | return hr; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | static HRESULT GetVariable( | ||
| 1381 | __in BURN_VARIABLES* pVariables, | ||
| 1382 | __in_z LPCWSTR wzVariable, | ||
| 1383 | __out BURN_VARIABLE** ppVariable | ||
| 1384 | ) | ||
| 1385 | { | ||
| 1386 | HRESULT hr = S_OK; | ||
| 1387 | DWORD iVariable = 0; | ||
| 1388 | BURN_VARIABLE* pVariable = NULL; | ||
| 1389 | |||
| 1390 | hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); | ||
| 1391 | ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); | ||
| 1392 | |||
| 1393 | if (S_FALSE == hr) | ||
| 1394 | { | ||
| 1395 | ExitFunction1(hr = E_NOTFOUND); | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | pVariable = &pVariables->rgVariables[iVariable]; | ||
| 1399 | |||
| 1400 | // initialize built-in variable | ||
| 1401 | if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType) | ||
| 1402 | { | ||
| 1403 | hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value); | ||
| 1404 | ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable); | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | *ppVariable = pVariable; | ||
| 1408 | |||
| 1409 | LExit: | ||
| 1410 | return hr; | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | static HRESULT FindVariableIndexByName( | ||
| 1414 | __in BURN_VARIABLES* pVariables, | ||
| 1415 | __in_z LPCWSTR wzVariable, | ||
| 1416 | __out DWORD* piVariable | ||
| 1417 | ) | ||
| 1418 | { | ||
| 1419 | HRESULT hr = S_OK; | ||
| 1420 | DWORD iRangeFirst = 0; | ||
| 1421 | DWORD cRangeLength = pVariables->cVariables; | ||
| 1422 | |||
| 1423 | while (cRangeLength) | ||
| 1424 | { | ||
| 1425 | // get variable in middle of range | ||
| 1426 | DWORD iPosition = cRangeLength / 2; | ||
| 1427 | BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition]; | ||
| 1428 | |||
| 1429 | switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1)) | ||
| 1430 | { | ||
| 1431 | case CSTR_LESS_THAN: | ||
| 1432 | // restrict range to elements before the current | ||
| 1433 | cRangeLength = iPosition; | ||
| 1434 | break; | ||
| 1435 | case CSTR_EQUAL: | ||
| 1436 | // variable found | ||
| 1437 | *piVariable = iRangeFirst + iPosition; | ||
| 1438 | ExitFunction1(hr = S_OK); | ||
| 1439 | case CSTR_GREATER_THAN: | ||
| 1440 | // restrict range to elements after the current | ||
| 1441 | iRangeFirst += iPosition + 1; | ||
| 1442 | cRangeLength -= iPosition + 1; | ||
| 1443 | break; | ||
| 1444 | default: | ||
| 1445 | ExitWithLastError(hr, "Failed to compare strings."); | ||
| 1446 | } | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | *piVariable = iRangeFirst; | ||
| 1450 | hr = S_FALSE; // variable not found | ||
| 1451 | |||
| 1452 | LExit: | ||
| 1453 | return hr; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static HRESULT InsertVariable( | ||
| 1457 | __in BURN_VARIABLES* pVariables, | ||
| 1458 | __in_z LPCWSTR wzVariable, | ||
| 1459 | __in DWORD iPosition | ||
| 1460 | ) | ||
| 1461 | { | ||
| 1462 | HRESULT hr = S_OK; | ||
| 1463 | size_t cbAllocSize = 0; | ||
| 1464 | |||
| 1465 | // ensure there is room in the variable array | ||
| 1466 | if (pVariables->cVariables == pVariables->dwMaxVariables) | ||
| 1467 | { | ||
| 1468 | hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables)); | ||
| 1469 | ExitOnRootFailure(hr, "Overflow while growing variable array size"); | ||
| 1470 | |||
| 1471 | if (pVariables->rgVariables) | ||
| 1472 | { | ||
| 1473 | hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize); | ||
| 1474 | ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer"); | ||
| 1475 | |||
| 1476 | LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE); | ||
| 1477 | ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables."); | ||
| 1478 | |||
| 1479 | // Prefast claims it's possible to hit this. Putting the check in just in case. | ||
| 1480 | if (pVariables->dwMaxVariables < pVariables->cVariables) | ||
| 1481 | { | ||
| 1482 | hr = INTSAFE_E_ARITHMETIC_OVERFLOW; | ||
| 1483 | ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation"); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | pVariables->rgVariables = (BURN_VARIABLE*)pv; | ||
| 1487 | memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables)); | ||
| 1488 | } | ||
| 1489 | else | ||
| 1490 | { | ||
| 1491 | pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE); | ||
| 1492 | ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables."); | ||
| 1493 | } | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | // move variables | ||
| 1497 | if (0 < pVariables->cVariables - iPosition) | ||
| 1498 | { | ||
| 1499 | memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition)); | ||
| 1500 | memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE)); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | ++pVariables->cVariables; | ||
| 1504 | |||
| 1505 | // allocate name | ||
| 1506 | hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0); | ||
| 1507 | ExitOnFailure(hr, "Failed to copy variable name."); | ||
| 1508 | |||
| 1509 | LExit: | ||
| 1510 | return hr; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | static HRESULT SetVariableValue( | ||
| 1514 | __in BURN_VARIABLES* pVariables, | ||
| 1515 | __in_z LPCWSTR wzVariable, | ||
| 1516 | __in BURN_VARIANT* pVariant, | ||
| 1517 | __in BOOL fLiteral, | ||
| 1518 | __in SET_VARIABLE setBuiltin, | ||
| 1519 | __in BOOL fLog | ||
| 1520 | ) | ||
| 1521 | { | ||
| 1522 | HRESULT hr = S_OK; | ||
| 1523 | DWORD iVariable = 0; | ||
| 1524 | |||
| 1525 | ::EnterCriticalSection(&pVariables->csAccess); | ||
| 1526 | |||
| 1527 | hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); | ||
| 1528 | ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); | ||
| 1529 | |||
| 1530 | // Insert element if not found. | ||
| 1531 | if (S_FALSE == hr) | ||
| 1532 | { | ||
| 1533 | hr = InsertVariable(pVariables, wzVariable, iVariable); | ||
| 1534 | ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable); | ||
| 1535 | } | ||
| 1536 | else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden. | ||
| 1537 | { | ||
| 1538 | if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin || | ||
| 1539 | (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) || | ||
| 1540 | SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType) | ||
| 1541 | { | ||
| 1542 | hr = S_OK; | ||
| 1543 | } | ||
| 1544 | else | ||
| 1545 | { | ||
| 1546 | hr = E_INVALIDARG; | ||
| 1547 | ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable); | ||
| 1548 | } | ||
| 1549 | } | ||
| 1550 | else // must *not* be a built-in variable so caller should not have tried to override it as a built-in. | ||
| 1551 | { | ||
| 1552 | // Not possible from external callers so just assert. | ||
| 1553 | AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable."); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | // Log value when not overwriting a built-in variable. | ||
| 1557 | if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType) | ||
| 1558 | { | ||
| 1559 | if (pVariables->rgVariables[iVariable].fHidden) | ||
| 1560 | { | ||
| 1561 | LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable); | ||
| 1562 | } | ||
| 1563 | else | ||
| 1564 | { | ||
| 1565 | // Assume value isn't encrypted since it's not hidden. | ||
| 1566 | switch (pVariant->Type) | ||
| 1567 | { | ||
| 1568 | case BURN_VARIANT_TYPE_NONE: | ||
| 1569 | if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) | ||
| 1570 | { | ||
| 1571 | LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); | ||
| 1572 | } | ||
| 1573 | break; | ||
| 1574 | |||
| 1575 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 1576 | LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); | ||
| 1577 | break; | ||
| 1578 | |||
| 1579 | case BURN_VARIANT_TYPE_STRING: | ||
| 1580 | if (!pVariant->sczValue) | ||
| 1581 | { | ||
| 1582 | LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue); | ||
| 1583 | } | ||
| 1584 | else | ||
| 1585 | { | ||
| 1586 | LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue); | ||
| 1587 | } | ||
| 1588 | break; | ||
| 1589 | |||
| 1590 | case BURN_VARIANT_TYPE_VERSION: | ||
| 1591 | LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%hu.%hu.%hu.%hu'", wzVariable, (WORD)(pVariant->qwValue >> 48), (WORD)(pVariant->qwValue >> 32), (WORD)(pVariant->qwValue >> 16), (WORD)(pVariant->qwValue)); | ||
| 1592 | break; | ||
| 1593 | |||
| 1594 | default: | ||
| 1595 | AssertSz(FALSE, "Unknown variant type."); | ||
| 1596 | break; | ||
| 1597 | } | ||
| 1598 | } | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | // Update variable value. | ||
| 1602 | hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); | ||
| 1603 | ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); | ||
| 1604 | |||
| 1605 | // Update variable literal flag. | ||
| 1606 | pVariables->rgVariables[iVariable].fLiteral = fLiteral; | ||
| 1607 | |||
| 1608 | LExit: | ||
| 1609 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
| 1610 | |||
| 1611 | if (FAILED(hr) && fLog) | ||
| 1612 | { | ||
| 1613 | LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr); | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | return hr; | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); | ||
| 1620 | |||
| 1621 | static HRESULT InitializeVariableVersionNT( | ||
| 1622 | __in DWORD_PTR dwpData, | ||
| 1623 | __inout BURN_VARIANT* pValue | ||
| 1624 | ) | ||
| 1625 | { | ||
| 1626 | HRESULT hr = S_OK; | ||
| 1627 | HMODULE ntdll = NULL; | ||
| 1628 | RTL_GET_VERSION rtlGetVersion = NULL; | ||
| 1629 | RTL_OSVERSIONINFOEXW ovix = { }; | ||
| 1630 | BURN_VARIANT value = { }; | ||
| 1631 | |||
| 1632 | if (!::GetModuleHandleExW(0, L"ntdll", &ntdll)) | ||
| 1633 | { | ||
| 1634 | ExitWithLastError(hr, "Failed to locate NTDLL."); | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | rtlGetVersion = reinterpret_cast<RTL_GET_VERSION>(::GetProcAddress(ntdll, "RtlGetVersion")); | ||
| 1638 | if (NULL == rtlGetVersion) | ||
| 1639 | { | ||
| 1640 | ExitWithLastError(hr, "Failed to locate RtlGetVersion."); | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | ovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); | ||
| 1644 | hr = static_cast<HRESULT>(rtlGetVersion(&ovix)); | ||
| 1645 | ExitOnFailure(hr, "Failed to get OS info."); | ||
| 1646 | |||
| 1647 | switch ((OS_INFO_VARIABLE)dwpData) | ||
| 1648 | { | ||
| 1649 | case OS_INFO_VARIABLE_ServicePackLevel: | ||
| 1650 | if (0 != ovix.wServicePackMajor) | ||
| 1651 | { | ||
| 1652 | value.qwValue = static_cast<DWORD64>(ovix.wServicePackMajor); | ||
| 1653 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1654 | } | ||
| 1655 | break; | ||
| 1656 | case OS_INFO_VARIABLE_VersionNT: | ||
| 1657 | value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); | ||
| 1658 | value.Type = BURN_VARIANT_TYPE_VERSION; | ||
| 1659 | break; | ||
| 1660 | case OS_INFO_VARIABLE_VersionNT64: | ||
| 1661 | { | ||
| 1662 | #if !defined(_WIN64) | ||
| 1663 | BOOL fIsWow64 = FALSE; | ||
| 1664 | |||
| 1665 | ProcWow64(::GetCurrentProcess(), &fIsWow64); | ||
| 1666 | if (fIsWow64) | ||
| 1667 | #endif | ||
| 1668 | { | ||
| 1669 | value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0); | ||
| 1670 | value.Type = BURN_VARIANT_TYPE_VERSION; | ||
| 1671 | } | ||
| 1672 | } | ||
| 1673 | break; | ||
| 1674 | default: | ||
| 1675 | AssertSz(FALSE, "Unknown OS info type."); | ||
| 1676 | break; | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | hr = BVariantCopy(&value, pValue); | ||
| 1680 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1681 | |||
| 1682 | LExit: | ||
| 1683 | if (NULL != ntdll) | ||
| 1684 | { | ||
| 1685 | FreeLibrary(ntdll); | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | return hr; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | static HRESULT InitializeVariableOsInfo( | ||
| 1692 | __in DWORD_PTR dwpData, | ||
| 1693 | __inout BURN_VARIANT* pValue | ||
| 1694 | ) | ||
| 1695 | { | ||
| 1696 | HRESULT hr = S_OK; | ||
| 1697 | OSVERSIONINFOEXW ovix = { }; | ||
| 1698 | BURN_VARIANT value = { }; | ||
| 1699 | |||
| 1700 | ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); | ||
| 1701 | if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) | ||
| 1702 | { | ||
| 1703 | ExitWithLastError(hr, "Failed to get OS info."); | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | switch ((OS_INFO_VARIABLE)dwpData) | ||
| 1707 | { | ||
| 1708 | case OS_INFO_VARIABLE_NTProductType: | ||
| 1709 | value.llValue = ovix.wProductType; | ||
| 1710 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1711 | break; | ||
| 1712 | case OS_INFO_VARIABLE_NTSuiteBackOffice: | ||
| 1713 | value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0; | ||
| 1714 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1715 | break; | ||
| 1716 | case OS_INFO_VARIABLE_NTSuiteDataCenter: | ||
| 1717 | value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0; | ||
| 1718 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1719 | break; | ||
| 1720 | case OS_INFO_VARIABLE_NTSuiteEnterprise: | ||
| 1721 | value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0; | ||
| 1722 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1723 | break; | ||
| 1724 | case OS_INFO_VARIABLE_NTSuitePersonal: | ||
| 1725 | value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0; | ||
| 1726 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1727 | break; | ||
| 1728 | case OS_INFO_VARIABLE_NTSuiteSmallBusiness: | ||
| 1729 | value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0; | ||
| 1730 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1731 | break; | ||
| 1732 | case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted: | ||
| 1733 | value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0; | ||
| 1734 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1735 | break; | ||
| 1736 | case OS_INFO_VARIABLE_NTSuiteWebServer: | ||
| 1737 | value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0; | ||
| 1738 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1739 | break; | ||
| 1740 | case OS_INFO_VARIABLE_CompatibilityMode: | ||
| 1741 | { | ||
| 1742 | DWORDLONG dwlConditionMask = 0; | ||
| 1743 | VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); | ||
| 1744 | VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); | ||
| 1745 | VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL); | ||
| 1746 | VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL); | ||
| 1747 | |||
| 1748 | value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask); | ||
| 1749 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1750 | } | ||
| 1751 | break; | ||
| 1752 | case OS_INFO_VARIABLE_TerminalServer: | ||
| 1753 | value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0; | ||
| 1754 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1755 | break; | ||
| 1756 | default: | ||
| 1757 | AssertSz(FALSE, "Unknown OS info type."); | ||
| 1758 | break; | ||
| 1759 | } | ||
| 1760 | |||
| 1761 | hr = BVariantCopy(&value, pValue); | ||
| 1762 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1763 | |||
| 1764 | LExit: | ||
| 1765 | return hr; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | static HRESULT InitializeVariableSystemInfo( | ||
| 1769 | __in DWORD_PTR dwpData, | ||
| 1770 | __inout BURN_VARIANT* pValue | ||
| 1771 | ) | ||
| 1772 | { | ||
| 1773 | HRESULT hr = S_OK; | ||
| 1774 | SYSTEM_INFO si = { }; | ||
| 1775 | BURN_VARIANT value = { }; | ||
| 1776 | |||
| 1777 | ::GetNativeSystemInfo(&si); | ||
| 1778 | |||
| 1779 | switch ((OS_INFO_VARIABLE)dwpData) | ||
| 1780 | { | ||
| 1781 | case OS_INFO_VARIABLE_ProcessorArchitecture: | ||
| 1782 | value.llValue = si.wProcessorArchitecture; | ||
| 1783 | value.Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 1784 | break; | ||
| 1785 | default: | ||
| 1786 | AssertSz(FALSE, "Unknown OS info type."); | ||
| 1787 | break; | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | hr = BVariantCopy(&value, pValue); | ||
| 1791 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1792 | |||
| 1793 | LExit: | ||
| 1794 | return hr; | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | static HRESULT InitializeVariableComputerName( | ||
| 1798 | __in DWORD_PTR dwpData, | ||
| 1799 | __inout BURN_VARIANT* pValue | ||
| 1800 | ) | ||
| 1801 | { | ||
| 1802 | UNREFERENCED_PARAMETER(dwpData); | ||
| 1803 | |||
| 1804 | HRESULT hr = S_OK; | ||
| 1805 | WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; | ||
| 1806 | DWORD cchComputerName = countof(wzComputerName); | ||
| 1807 | |||
| 1808 | // get computer name | ||
| 1809 | if (!::GetComputerNameW(wzComputerName, &cchComputerName)) | ||
| 1810 | { | ||
| 1811 | ExitWithLastError(hr, "Failed to get computer name."); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | // set value | ||
| 1815 | hr = BVariantSetString(pValue, wzComputerName, 0); | ||
| 1816 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1817 | |||
| 1818 | LExit: | ||
| 1819 | return hr; | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | static HRESULT InitializeVariableVersionMsi( | ||
| 1823 | __in DWORD_PTR dwpData, | ||
| 1824 | __inout BURN_VARIANT* pValue | ||
| 1825 | ) | ||
| 1826 | { | ||
| 1827 | UNREFERENCED_PARAMETER(dwpData); | ||
| 1828 | |||
| 1829 | HRESULT hr = S_OK; | ||
| 1830 | DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; | ||
| 1831 | DLLVERSIONINFO msiVersionInfo = { }; | ||
| 1832 | |||
| 1833 | // get DllGetVersion proc address | ||
| 1834 | pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); | ||
| 1835 | ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll."); | ||
| 1836 | |||
| 1837 | // get msi.dll version info | ||
| 1838 | msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO); | ||
| 1839 | hr = pfnMsiDllGetVersion(&msiVersionInfo); | ||
| 1840 | ExitOnFailure(hr, "Failed to get msi.dll version info."); | ||
| 1841 | |||
| 1842 | hr = BVariantSetVersion(pValue, MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0)); | ||
| 1843 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1844 | |||
| 1845 | LExit: | ||
| 1846 | return hr; | ||
| 1847 | } | ||
| 1848 | |||
| 1849 | static HRESULT InitializeVariableCsidlFolder( | ||
| 1850 | __in DWORD_PTR dwpData, | ||
| 1851 | __inout BURN_VARIANT* pValue | ||
| 1852 | ) | ||
| 1853 | { | ||
| 1854 | HRESULT hr = S_OK; | ||
| 1855 | LPWSTR sczPath = NULL; | ||
| 1856 | int nFolder = (int)dwpData; | ||
| 1857 | |||
| 1858 | // get folder path | ||
| 1859 | hr = ShelGetFolder(&sczPath, nFolder); | ||
| 1860 | ExitOnRootFailure(hr, "Failed to get shell folder."); | ||
| 1861 | |||
| 1862 | // set value | ||
| 1863 | hr = BVariantSetString(pValue, sczPath, 0); | ||
| 1864 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1865 | |||
| 1866 | LExit: | ||
| 1867 | ReleaseStr(sczPath); | ||
| 1868 | |||
| 1869 | return hr; | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | static HRESULT InitializeVariableTempFolder( | ||
| 1873 | __in DWORD_PTR dwpData, | ||
| 1874 | __inout BURN_VARIANT* pValue | ||
| 1875 | ) | ||
| 1876 | { | ||
| 1877 | UNREFERENCED_PARAMETER(dwpData); | ||
| 1878 | |||
| 1879 | HRESULT hr = S_OK; | ||
| 1880 | WCHAR wzPath[MAX_PATH] = { }; | ||
| 1881 | |||
| 1882 | // get volume path name | ||
| 1883 | if (!::GetTempPathW(MAX_PATH, wzPath)) | ||
| 1884 | { | ||
| 1885 | ExitWithLastError(hr, "Failed to get temp path."); | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | // set value | ||
| 1889 | hr = BVariantSetString(pValue, wzPath, 0); | ||
| 1890 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1891 | |||
| 1892 | LExit: | ||
| 1893 | return hr; | ||
| 1894 | } | ||
| 1895 | |||
| 1896 | static HRESULT InitializeVariableSystemFolder( | ||
| 1897 | __in DWORD_PTR dwpData, | ||
| 1898 | __inout BURN_VARIANT* pValue | ||
| 1899 | ) | ||
| 1900 | { | ||
| 1901 | HRESULT hr = S_OK; | ||
| 1902 | BOOL f64 = (BOOL)dwpData; | ||
| 1903 | WCHAR wzSystemFolder[MAX_PATH] = { }; | ||
| 1904 | |||
| 1905 | #if !defined(_WIN64) | ||
| 1906 | BOOL fIsWow64 = FALSE; | ||
| 1907 | ProcWow64(::GetCurrentProcess(), &fIsWow64); | ||
| 1908 | |||
| 1909 | if (fIsWow64) | ||
| 1910 | { | ||
| 1911 | if (f64) | ||
| 1912 | { | ||
| 1913 | if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) | ||
| 1914 | { | ||
| 1915 | ExitWithLastError(hr, "Failed to get 64-bit system folder."); | ||
| 1916 | } | ||
| 1917 | } | ||
| 1918 | else | ||
| 1919 | { | ||
| 1920 | if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) | ||
| 1921 | { | ||
| 1922 | ExitWithLastError(hr, "Failed to get 32-bit system folder."); | ||
| 1923 | } | ||
| 1924 | } | ||
| 1925 | } | ||
| 1926 | else | ||
| 1927 | { | ||
| 1928 | if (!f64) | ||
| 1929 | { | ||
| 1930 | if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) | ||
| 1931 | { | ||
| 1932 | ExitWithLastError(hr, "Failed to get 32-bit system folder."); | ||
| 1933 | } | ||
| 1934 | } | ||
| 1935 | } | ||
| 1936 | #else | ||
| 1937 | if (f64) | ||
| 1938 | { | ||
| 1939 | if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) | ||
| 1940 | { | ||
| 1941 | ExitWithLastError(hr, "Failed to get 64-bit system folder."); | ||
| 1942 | } | ||
| 1943 | } | ||
| 1944 | else | ||
| 1945 | { | ||
| 1946 | if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) | ||
| 1947 | { | ||
| 1948 | ExitWithLastError(hr, "Failed to get 32-bit system folder."); | ||
| 1949 | } | ||
| 1950 | } | ||
| 1951 | #endif | ||
| 1952 | |||
| 1953 | if (*wzSystemFolder) | ||
| 1954 | { | ||
| 1955 | hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder)); | ||
| 1956 | ExitOnFailure(hr, "Failed to backslash terminate system folder."); | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | // set value | ||
| 1960 | hr = BVariantSetString(pValue, wzSystemFolder, 0); | ||
| 1961 | ExitOnFailure(hr, "Failed to set system folder variant value."); | ||
| 1962 | |||
| 1963 | LExit: | ||
| 1964 | return hr; | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | static HRESULT InitializeVariableWindowsVolumeFolder( | ||
| 1968 | __in DWORD_PTR dwpData, | ||
| 1969 | __inout BURN_VARIANT* pValue | ||
| 1970 | ) | ||
| 1971 | { | ||
| 1972 | UNREFERENCED_PARAMETER(dwpData); | ||
| 1973 | |||
| 1974 | HRESULT hr = S_OK; | ||
| 1975 | WCHAR wzWindowsPath[MAX_PATH] = { }; | ||
| 1976 | WCHAR wzVolumePath[MAX_PATH] = { }; | ||
| 1977 | |||
| 1978 | // get windows directory | ||
| 1979 | if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath))) | ||
| 1980 | { | ||
| 1981 | ExitWithLastError(hr, "Failed to get windows directory."); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | // get volume path name | ||
| 1985 | if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH)) | ||
| 1986 | { | ||
| 1987 | ExitWithLastError(hr, "Failed to get volume path name."); | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | // set value | ||
| 1991 | hr = BVariantSetString(pValue, wzVolumePath, 0); | ||
| 1992 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 1993 | |||
| 1994 | LExit: | ||
| 1995 | return hr; | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | static HRESULT InitializeVariablePrivileged( | ||
| 1999 | __in DWORD_PTR dwpData, | ||
| 2000 | __inout BURN_VARIANT* pValue | ||
| 2001 | ) | ||
| 2002 | { | ||
| 2003 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2004 | |||
| 2005 | HRESULT hr = S_OK; | ||
| 2006 | BOOL fPrivileged = FALSE; | ||
| 2007 | |||
| 2008 | // check if process could run privileged. | ||
| 2009 | hr = OsCouldRunPrivileged(&fPrivileged); | ||
| 2010 | ExitOnFailure(hr, "Failed to check if process could run privileged."); | ||
| 2011 | |||
| 2012 | // set value | ||
| 2013 | hr = BVariantSetNumeric(pValue, fPrivileged); | ||
| 2014 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2015 | |||
| 2016 | LExit: | ||
| 2017 | return hr; | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | static HRESULT InitializeVariableRebootPending( | ||
| 2021 | __in DWORD_PTR dwpData, | ||
| 2022 | __inout BURN_VARIANT* pValue | ||
| 2023 | ) | ||
| 2024 | { | ||
| 2025 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2026 | |||
| 2027 | HRESULT hr = S_OK; | ||
| 2028 | BOOL fRebootPending = FALSE; | ||
| 2029 | BOOL fComInitialized = FALSE; | ||
| 2030 | |||
| 2031 | // Do a best effort to ask WU if a reboot is required. If anything goes | ||
| 2032 | // wrong then let's pretend a reboot is not required. | ||
| 2033 | hr = ::CoInitialize(NULL); | ||
| 2034 | if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) | ||
| 2035 | { | ||
| 2036 | fComInitialized = TRUE; | ||
| 2037 | |||
| 2038 | hr = WuaRestartRequired(&fRebootPending); | ||
| 2039 | if (FAILED(hr)) | ||
| 2040 | { | ||
| 2041 | fRebootPending = FALSE; | ||
| 2042 | hr = S_OK; | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | hr = BVariantSetNumeric(pValue, fRebootPending); | ||
| 2047 | ExitOnFailure(hr, "Failed to set reboot pending variant value."); | ||
| 2048 | |||
| 2049 | LExit: | ||
| 2050 | if (fComInitialized) | ||
| 2051 | { | ||
| 2052 | ::CoUninitialize(); | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | return hr; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | static HRESULT InitializeSystemLanguageID( | ||
| 2059 | __in DWORD_PTR dwpData, | ||
| 2060 | __inout BURN_VARIANT* pValue | ||
| 2061 | ) | ||
| 2062 | { | ||
| 2063 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2064 | |||
| 2065 | HRESULT hr = S_OK; | ||
| 2066 | LANGID langid = ::GetSystemDefaultLangID(); | ||
| 2067 | |||
| 2068 | hr = BVariantSetNumeric(pValue, langid); | ||
| 2069 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2070 | |||
| 2071 | LExit: | ||
| 2072 | return hr; | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | static HRESULT InitializeUserUILanguageID( | ||
| 2076 | __in DWORD_PTR dwpData, | ||
| 2077 | __inout BURN_VARIANT* pValue | ||
| 2078 | ) | ||
| 2079 | { | ||
| 2080 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2081 | |||
| 2082 | HRESULT hr = S_OK; | ||
| 2083 | LANGID langid = ::GetUserDefaultUILanguage(); | ||
| 2084 | |||
| 2085 | hr = BVariantSetNumeric(pValue, langid); | ||
| 2086 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2087 | |||
| 2088 | LExit: | ||
| 2089 | return hr; | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | static HRESULT InitializeUserLanguageID( | ||
| 2093 | __in DWORD_PTR dwpData, | ||
| 2094 | __inout BURN_VARIANT* pValue | ||
| 2095 | ) | ||
| 2096 | { | ||
| 2097 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2098 | |||
| 2099 | HRESULT hr = S_OK; | ||
| 2100 | LANGID langid = ::GetUserDefaultLangID(); | ||
| 2101 | |||
| 2102 | hr = BVariantSetNumeric(pValue, langid); | ||
| 2103 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2104 | |||
| 2105 | LExit: | ||
| 2106 | return hr; | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | static HRESULT InitializeVariableString( | ||
| 2110 | __in DWORD_PTR dwpData, | ||
| 2111 | __inout BURN_VARIANT* pValue | ||
| 2112 | ) | ||
| 2113 | { | ||
| 2114 | HRESULT hr = S_OK; | ||
| 2115 | LPCWSTR wzValue = (LPCWSTR)dwpData; | ||
| 2116 | |||
| 2117 | // set value | ||
| 2118 | hr = BVariantSetString(pValue, wzValue, 0); | ||
| 2119 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2120 | |||
| 2121 | LExit: | ||
| 2122 | return hr; | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | static HRESULT InitializeVariableNumeric( | ||
| 2126 | __in DWORD_PTR dwpData, | ||
| 2127 | __inout BURN_VARIANT* pValue | ||
| 2128 | ) | ||
| 2129 | { | ||
| 2130 | HRESULT hr = S_OK; | ||
| 2131 | LONGLONG llValue = (LONGLONG)dwpData; | ||
| 2132 | |||
| 2133 | // set value | ||
| 2134 | hr = BVariantSetNumeric(pValue, llValue); | ||
| 2135 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2136 | |||
| 2137 | LExit: | ||
| 2138 | return hr; | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | static HRESULT InitializeVariableRegistryFolder( | ||
| 2142 | __in DWORD_PTR dwpData, | ||
| 2143 | __inout BURN_VARIANT* pValue | ||
| 2144 | ) | ||
| 2145 | { | ||
| 2146 | HRESULT hr = S_OK; | ||
| 2147 | int nFolder = (int)dwpData; | ||
| 2148 | LPWSTR sczPath = NULL; | ||
| 2149 | |||
| 2150 | #if !defined(_WIN64) | ||
| 2151 | BOOL fIsWow64 = FALSE; | ||
| 2152 | |||
| 2153 | ProcWow64(::GetCurrentProcess(), &fIsWow64); | ||
| 2154 | if (!fIsWow64) // on 32-bit machines, variables aren't set | ||
| 2155 | { | ||
| 2156 | ExitFunction(); | ||
| 2157 | } | ||
| 2158 | #endif | ||
| 2159 | |||
| 2160 | hr = Get64bitFolderFromRegistry(nFolder, &sczPath); | ||
| 2161 | ExitOnFailure(hr, "Failed to get 64-bit folder."); | ||
| 2162 | |||
| 2163 | // set value | ||
| 2164 | hr = BVariantSetString(pValue, sczPath, 0); | ||
| 2165 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2166 | |||
| 2167 | LExit: | ||
| 2168 | ReleaseStr(sczPath); | ||
| 2169 | |||
| 2170 | return hr; | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | static HRESULT InitializeVariable6432Folder( | ||
| 2174 | __in DWORD_PTR dwpData, | ||
| 2175 | __inout BURN_VARIANT* pValue | ||
| 2176 | ) | ||
| 2177 | { | ||
| 2178 | HRESULT hr = S_OK; | ||
| 2179 | int nFolder = (int)dwpData; | ||
| 2180 | LPWSTR sczPath = NULL; | ||
| 2181 | |||
| 2182 | #if !defined(_WIN64) | ||
| 2183 | BOOL fIsWow64 = FALSE; | ||
| 2184 | |||
| 2185 | // If 32-bit use shell-folder. | ||
| 2186 | ProcWow64(::GetCurrentProcess(), &fIsWow64); | ||
| 2187 | if (!fIsWow64) | ||
| 2188 | { | ||
| 2189 | hr = ShelGetFolder(&sczPath, nFolder); | ||
| 2190 | ExitOnRootFailure(hr, "Failed to get shell folder."); | ||
| 2191 | } | ||
| 2192 | else | ||
| 2193 | #endif | ||
| 2194 | { | ||
| 2195 | hr = Get64bitFolderFromRegistry(nFolder, &sczPath); | ||
| 2196 | ExitOnFailure(hr, "Failed to get 64-bit folder."); | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | // set value | ||
| 2200 | hr = BVariantSetString(pValue, sczPath, 0); | ||
| 2201 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2202 | |||
| 2203 | LExit: | ||
| 2204 | ReleaseStr(sczPath); | ||
| 2205 | |||
| 2206 | return hr; | ||
| 2207 | } | ||
| 2208 | |||
| 2209 | // Get the date in the same format as Windows Installer. | ||
| 2210 | static HRESULT InitializeVariableDate( | ||
| 2211 | __in DWORD_PTR /*dwpData*/, | ||
| 2212 | __inout BURN_VARIANT* pValue | ||
| 2213 | ) | ||
| 2214 | { | ||
| 2215 | HRESULT hr = S_OK; | ||
| 2216 | SYSTEMTIME systime = { }; | ||
| 2217 | LPWSTR sczDate = NULL; | ||
| 2218 | int cchDate = 0; | ||
| 2219 | |||
| 2220 | ::GetSystemTime(&systime); | ||
| 2221 | |||
| 2222 | cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate); | ||
| 2223 | if (!cchDate) | ||
| 2224 | { | ||
| 2225 | ExitOnLastError(hr, "Failed to get the required buffer length for the Date."); | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | hr = StrAlloc(&sczDate, cchDate); | ||
| 2229 | ExitOnFailure(hr, "Failed to allocate the buffer for the Date."); | ||
| 2230 | |||
| 2231 | if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate)) | ||
| 2232 | { | ||
| 2233 | ExitOnLastError(hr, "Failed to get the Date."); | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | // set value | ||
| 2237 | hr = BVariantSetString(pValue, sczDate, cchDate); | ||
| 2238 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2239 | |||
| 2240 | LExit: | ||
| 2241 | ReleaseStr(sczDate); | ||
| 2242 | |||
| 2243 | return hr; | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | static HRESULT InitializeVariableInstallerName( | ||
| 2247 | __in DWORD_PTR /*dwpData*/, | ||
| 2248 | __inout BURN_VARIANT* pValue | ||
| 2249 | ) | ||
| 2250 | { | ||
| 2251 | HRESULT hr = S_OK; | ||
| 2252 | |||
| 2253 | // set value | ||
| 2254 | hr = BVariantSetString(pValue, L"WiX Burn", 0); | ||
| 2255 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2256 | |||
| 2257 | LExit: | ||
| 2258 | return hr; | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | static HRESULT InitializeVariableInstallerVersion( | ||
| 2262 | __in DWORD_PTR /*dwpData*/, | ||
| 2263 | __inout BURN_VARIANT* pValue | ||
| 2264 | ) | ||
| 2265 | { | ||
| 2266 | HRESULT hr = S_OK; | ||
| 2267 | LPWSTR sczVersion = NULL; | ||
| 2268 | |||
| 2269 | hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP); | ||
| 2270 | ExitOnFailure(hr, "Failed to copy the engine version."); | ||
| 2271 | |||
| 2272 | // set value | ||
| 2273 | hr = BVariantSetString(pValue, sczVersion, 0); | ||
| 2274 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2275 | |||
| 2276 | LExit: | ||
| 2277 | ReleaseStr(sczVersion); | ||
| 2278 | |||
| 2279 | return hr; | ||
| 2280 | } | ||
| 2281 | |||
| 2282 | static HRESULT InitializeVariableVersion( | ||
| 2283 | __in DWORD_PTR dwpData, | ||
| 2284 | __inout BURN_VARIANT* pValue | ||
| 2285 | ) | ||
| 2286 | { | ||
| 2287 | HRESULT hr = S_OK; | ||
| 2288 | |||
| 2289 | // set value | ||
| 2290 | hr = BVariantSetVersion(pValue, static_cast<DWORD64>(dwpData)); | ||
| 2291 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2292 | |||
| 2293 | LExit: | ||
| 2294 | return hr; | ||
| 2295 | } | ||
| 2296 | |||
| 2297 | // Get the current user the same as Windows Installer. | ||
| 2298 | static HRESULT InitializeVariableLogonUser( | ||
| 2299 | __in DWORD_PTR /*dwpData*/, | ||
| 2300 | __inout BURN_VARIANT* pValue | ||
| 2301 | ) | ||
| 2302 | { | ||
| 2303 | HRESULT hr = S_OK; | ||
| 2304 | WCHAR wzUserName[UNLEN + 1]; | ||
| 2305 | DWORD cchUserName = countof(wzUserName); | ||
| 2306 | |||
| 2307 | if (!::GetUserNameW(wzUserName, &cchUserName)) | ||
| 2308 | { | ||
| 2309 | ExitOnLastError(hr, "Failed to get the user name."); | ||
| 2310 | } | ||
| 2311 | |||
| 2312 | // set value | ||
| 2313 | hr = BVariantSetString(pValue, wzUserName, 0); | ||
| 2314 | ExitOnFailure(hr, "Failed to set variant value."); | ||
| 2315 | |||
| 2316 | LExit: | ||
| 2317 | return hr; | ||
| 2318 | } | ||
| 2319 | |||
| 2320 | static HRESULT Get64bitFolderFromRegistry( | ||
| 2321 | __in int nFolder, | ||
| 2322 | __deref_out_z LPWSTR* psczPath | ||
| 2323 | ) | ||
| 2324 | { | ||
| 2325 | HRESULT hr = S_OK; | ||
| 2326 | HKEY hkFolders = NULL; | ||
| 2327 | |||
| 2328 | AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL."); | ||
| 2329 | LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir"; | ||
| 2330 | |||
| 2331 | hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders); | ||
| 2332 | ExitOnFailure(hr, "Failed to open Windows folder key."); | ||
| 2333 | |||
| 2334 | hr = RegReadString(hkFolders, wzFolderValue, psczPath); | ||
| 2335 | ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue); | ||
| 2336 | |||
| 2337 | hr = PathBackslashTerminate(psczPath); | ||
| 2338 | ExitOnFailure(hr, "Failed to ensure path was backslash terminated."); | ||
| 2339 | |||
| 2340 | LExit: | ||
| 2341 | ReleaseRegKey(hkFolders); | ||
| 2342 | |||
| 2343 | return hr; | ||
| 2344 | } | ||
| 2345 | |||
diff --git a/src/engine/variable.h b/src/engine/variable.h new file mode 100644 index 00000000..c48e0160 --- /dev/null +++ b/src/engine/variable.h | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | const LPCWSTR VARIABLE_DATE = L"Date"; | ||
| 13 | const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; | ||
| 14 | const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; | ||
| 15 | const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; | ||
| 16 | |||
| 17 | |||
| 18 | // typedefs | ||
| 19 | |||
| 20 | typedef HRESULT (*PFN_INITIALIZEVARIABLE)( | ||
| 21 | __in DWORD_PTR dwpData, | ||
| 22 | __inout BURN_VARIANT* pValue | ||
| 23 | ); | ||
| 24 | |||
| 25 | |||
| 26 | // constants | ||
| 27 | |||
| 28 | enum BURN_VARIABLE_INTERNAL_TYPE | ||
| 29 | { | ||
| 30 | BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable. | ||
| 31 | BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process. | ||
| 32 | BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process. | ||
| 33 | }; | ||
| 34 | |||
| 35 | |||
| 36 | // structs | ||
| 37 | |||
| 38 | typedef struct _BURN_VARIABLE | ||
| 39 | { | ||
| 40 | LPWSTR sczName; | ||
| 41 | BURN_VARIANT Value; | ||
| 42 | BOOL fHidden; | ||
| 43 | BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting). | ||
| 44 | BOOL fPersisted; | ||
| 45 | |||
| 46 | // used for late initialization of built-in variables | ||
| 47 | BURN_VARIABLE_INTERNAL_TYPE internalType; | ||
| 48 | PFN_INITIALIZEVARIABLE pfnInitialize; | ||
| 49 | DWORD_PTR dwpInitializeData; | ||
| 50 | } BURN_VARIABLE; | ||
| 51 | |||
| 52 | typedef struct _BURN_VARIABLES | ||
| 53 | { | ||
| 54 | CRITICAL_SECTION csAccess; | ||
| 55 | DWORD dwMaxVariables; | ||
| 56 | DWORD cVariables; | ||
| 57 | BURN_VARIABLE* rgVariables; | ||
| 58 | } BURN_VARIABLES; | ||
| 59 | |||
| 60 | |||
| 61 | // function declarations | ||
| 62 | |||
| 63 | HRESULT VariableInitialize( | ||
| 64 | __in BURN_VARIABLES* pVariables | ||
| 65 | ); | ||
| 66 | HRESULT VariablesParseFromXml( | ||
| 67 | __in BURN_VARIABLES* pVariables, | ||
| 68 | __in IXMLDOMNode* pixnBundle | ||
| 69 | ); | ||
| 70 | void VariablesUninitialize( | ||
| 71 | __in BURN_VARIABLES* pVariables | ||
| 72 | ); | ||
| 73 | void VariablesDump( | ||
| 74 | __in BURN_VARIABLES* pVariables | ||
| 75 | ); | ||
| 76 | HRESULT VariableGetNumeric( | ||
| 77 | __in BURN_VARIABLES* pVariables, | ||
| 78 | __in_z LPCWSTR wzVariable, | ||
| 79 | __out LONGLONG* pllValue | ||
| 80 | ); | ||
| 81 | HRESULT VariableGetString( | ||
| 82 | __in BURN_VARIABLES* pVariables, | ||
| 83 | __in_z LPCWSTR wzVariable, | ||
| 84 | __out_z LPWSTR* psczValue | ||
| 85 | ); | ||
| 86 | HRESULT VariableGetVersion( | ||
| 87 | __in BURN_VARIABLES* pVariables, | ||
| 88 | __in_z LPCWSTR wzVariable, | ||
| 89 | __in DWORD64* pqwValue | ||
| 90 | ); | ||
| 91 | HRESULT VariableGetVariant( | ||
| 92 | __in BURN_VARIABLES* pVariables, | ||
| 93 | __in_z LPCWSTR wzVariable, | ||
| 94 | __in BURN_VARIANT* pValue | ||
| 95 | ); | ||
| 96 | HRESULT VariableGetFormatted( | ||
| 97 | __in BURN_VARIABLES* pVariables, | ||
| 98 | __in_z LPCWSTR wzVariable, | ||
| 99 | __out_z LPWSTR* psczValue | ||
| 100 | ); | ||
| 101 | HRESULT VariableSetNumeric( | ||
| 102 | __in BURN_VARIABLES* pVariables, | ||
| 103 | __in_z LPCWSTR wzVariable, | ||
| 104 | __in LONGLONG llValue, | ||
| 105 | __in BOOL fOverwriteBuiltIn | ||
| 106 | ); | ||
| 107 | HRESULT VariableSetLiteralString( | ||
| 108 | __in BURN_VARIABLES* pVariables, | ||
| 109 | __in_z LPCWSTR wzVariable, | ||
| 110 | __in_z_opt LPCWSTR wzValue, | ||
| 111 | __in BOOL fOverwriteBuiltIn | ||
| 112 | ); | ||
| 113 | HRESULT VariableSetString( | ||
| 114 | __in BURN_VARIABLES* pVariables, | ||
| 115 | __in_z LPCWSTR wzVariable, | ||
| 116 | __in_z_opt LPCWSTR wzValue, | ||
| 117 | __in BOOL fOverwriteBuiltIn | ||
| 118 | ); | ||
| 119 | HRESULT VariableSetVersion( | ||
| 120 | __in BURN_VARIABLES* pVariables, | ||
| 121 | __in_z LPCWSTR wzVariable, | ||
| 122 | __in DWORD64 qwValue, | ||
| 123 | __in BOOL fOverwriteBuiltIn | ||
| 124 | ); | ||
| 125 | HRESULT VariableSetLiteralVariant( | ||
| 126 | __in BURN_VARIABLES* pVariables, | ||
| 127 | __in_z LPCWSTR wzVariable, | ||
| 128 | __in BURN_VARIANT* pVariant | ||
| 129 | ); | ||
| 130 | HRESULT VariableFormatString( | ||
| 131 | __in BURN_VARIABLES* pVariables, | ||
| 132 | __in_z LPCWSTR wzIn, | ||
| 133 | __out_z_opt LPWSTR* psczOut, | ||
| 134 | __out_opt DWORD* pcchOut | ||
| 135 | ); | ||
| 136 | HRESULT VariableFormatStringObfuscated( | ||
| 137 | __in BURN_VARIABLES* pVariables, | ||
| 138 | __in_z LPCWSTR wzIn, | ||
| 139 | __out_z_opt LPWSTR* psczOut, | ||
| 140 | __out_opt DWORD* pcchOut | ||
| 141 | ); | ||
| 142 | HRESULT VariableEscapeString( | ||
| 143 | __in_z LPCWSTR wzIn, | ||
| 144 | __out_z LPWSTR* psczOut | ||
| 145 | ); | ||
| 146 | HRESULT VariableSerialize( | ||
| 147 | __in BURN_VARIABLES* pVariables, | ||
| 148 | __in BOOL fPersisting, | ||
| 149 | __inout BYTE** ppbBuffer, | ||
| 150 | __inout SIZE_T* piBuffer | ||
| 151 | ); | ||
| 152 | HRESULT VariableDeserialize( | ||
| 153 | __in BURN_VARIABLES* pVariables, | ||
| 154 | __in BOOL fWasPersisted, | ||
| 155 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
| 156 | __in SIZE_T cbBuffer, | ||
| 157 | __inout SIZE_T* piBuffer | ||
| 158 | ); | ||
| 159 | HRESULT VariableStrAlloc( | ||
| 160 | __in BOOL fZeroOnRealloc, | ||
| 161 | __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, | ||
| 162 | __in DWORD_PTR cch | ||
| 163 | ); | ||
| 164 | HRESULT VariableStrAllocString( | ||
| 165 | __in BOOL fZeroOnRealloc, | ||
| 166 | __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, | ||
| 167 | __in_z LPCWSTR wzSource, | ||
| 168 | __in DWORD_PTR cchSource | ||
| 169 | ); | ||
| 170 | HRESULT VariableStrAllocConcat( | ||
| 171 | __in BOOL fZeroOnRealloc, | ||
| 172 | __deref_out_z LPWSTR* ppwz, | ||
| 173 | __in_z LPCWSTR wzSource, | ||
| 174 | __in DWORD_PTR cchSource | ||
| 175 | ); | ||
| 176 | HRESULT __cdecl VariableStrAllocFormatted( | ||
| 177 | __in BOOL fZeroOnRealloc, | ||
| 178 | __deref_out_z LPWSTR* ppwz, | ||
| 179 | __in __format_string LPCWSTR wzFormat, | ||
| 180 | ... | ||
| 181 | ); | ||
| 182 | HRESULT VariableIsHidden( | ||
| 183 | __in BURN_VARIABLES* pVariables, | ||
| 184 | __in_z LPCWSTR wzVariable, | ||
| 185 | __out BOOL* pfHidden | ||
| 186 | ); | ||
| 187 | |||
| 188 | #if defined(__cplusplus) | ||
| 189 | } | ||
| 190 | #endif | ||
diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp new file mode 100644 index 00000000..2a9f08ed --- /dev/null +++ b/src/engine/variant.cpp | |||
| @@ -0,0 +1,601 @@ | |||
| 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 | |||
| 5 | #define VARIANT_ENCRYPTION_SCOPE CRYPTPROTECTMEMORY_SAME_PROCESS | ||
| 6 | |||
| 7 | // internal function declarations | ||
| 8 | |||
| 9 | static HRESULT BVariantEncryptNumeric( | ||
| 10 | __in BURN_VARIANT* pVariant, | ||
| 11 | __in BOOL fEncrypt | ||
| 12 | ); | ||
| 13 | |||
| 14 | static HRESULT BVariantEncryptString( | ||
| 15 | __in BURN_VARIANT* pVariant, | ||
| 16 | __in BOOL fEncrypt | ||
| 17 | ); | ||
| 18 | |||
| 19 | static HRESULT BVariantEncryptVersion( | ||
| 20 | __in BURN_VARIANT* pVariant, | ||
| 21 | __in BOOL fEncrypt | ||
| 22 | ); | ||
| 23 | |||
| 24 | static HRESULT BVariantRetrieveDecryptedNumeric( | ||
| 25 | __in BURN_VARIANT* pVariant, | ||
| 26 | __out LONGLONG* pllValue | ||
| 27 | ); | ||
| 28 | |||
| 29 | static HRESULT BVariantRetrieveDecryptedString( | ||
| 30 | __in BURN_VARIANT* pVariant, | ||
| 31 | __out LPWSTR* psczValue | ||
| 32 | ); | ||
| 33 | |||
| 34 | static HRESULT BVariantRetrieveDecryptedVersion( | ||
| 35 | __in BURN_VARIANT* pVariant, | ||
| 36 | __out DWORD64* pqwValue | ||
| 37 | ); | ||
| 38 | |||
| 39 | // function definitions | ||
| 40 | |||
| 41 | extern "C" void BVariantUninitialize( | ||
| 42 | __in BURN_VARIANT* pVariant | ||
| 43 | ) | ||
| 44 | { | ||
| 45 | if (BURN_VARIANT_TYPE_STRING == pVariant->Type) | ||
| 46 | { | ||
| 47 | StrSecureZeroFreeString(pVariant->sczValue); | ||
| 48 | } | ||
| 49 | SecureZeroMemory(pVariant, sizeof(BURN_VARIANT)); | ||
| 50 | } | ||
| 51 | |||
| 52 | // The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
| 53 | extern "C" HRESULT BVariantGetNumeric( | ||
| 54 | __in BURN_VARIANT* pVariant, | ||
| 55 | __out LONGLONG* pllValue | ||
| 56 | ) | ||
| 57 | { | ||
| 58 | HRESULT hr = S_OK; | ||
| 59 | LPWSTR sczValue = NULL; | ||
| 60 | |||
| 61 | switch (pVariant->Type) | ||
| 62 | { | ||
| 63 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 64 | BVariantRetrieveDecryptedNumeric(pVariant, pllValue); | ||
| 65 | break; | ||
| 66 | case BURN_VARIANT_TYPE_STRING: | ||
| 67 | hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); | ||
| 68 | if (SUCCEEDED(hr)) | ||
| 69 | { | ||
| 70 | hr = StrStringToInt64(sczValue, 0, pllValue); | ||
| 71 | if (FAILED(hr)) | ||
| 72 | { | ||
| 73 | hr = DISP_E_TYPEMISMATCH; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | StrSecureZeroFreeString(sczValue); | ||
| 77 | break; | ||
| 78 | case BURN_VARIANT_TYPE_VERSION: | ||
| 79 | BVariantRetrieveDecryptedVersion(pVariant, (DWORD64*)pllValue); | ||
| 80 | break; | ||
| 81 | default: | ||
| 82 | hr = E_INVALIDARG; | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | |||
| 86 | return hr; | ||
| 87 | } | ||
| 88 | |||
| 89 | // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. | ||
| 90 | extern "C" HRESULT BVariantGetString( | ||
| 91 | __in BURN_VARIANT* pVariant, | ||
| 92 | __out_z LPWSTR* psczValue | ||
| 93 | ) | ||
| 94 | { | ||
| 95 | HRESULT hr = S_OK; | ||
| 96 | LONGLONG llValue = 0; | ||
| 97 | DWORD64 qwValue = 0; | ||
| 98 | |||
| 99 | switch (pVariant->Type) | ||
| 100 | { | ||
| 101 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 102 | hr = BVariantRetrieveDecryptedNumeric(pVariant, &llValue); | ||
| 103 | if (SUCCEEDED(hr)) | ||
| 104 | { | ||
| 105 | hr = StrAllocFormattedSecure(psczValue, L"%I64d", llValue); | ||
| 106 | ExitOnFailure(hr, "Failed to convert int64 to string."); | ||
| 107 | } | ||
| 108 | SecureZeroMemory(&llValue, sizeof(llValue)); | ||
| 109 | break; | ||
| 110 | case BURN_VARIANT_TYPE_STRING: | ||
| 111 | hr = BVariantRetrieveDecryptedString(pVariant, psczValue); | ||
| 112 | break; | ||
| 113 | case BURN_VARIANT_TYPE_VERSION: | ||
| 114 | hr = BVariantRetrieveDecryptedVersion(pVariant, &qwValue); | ||
| 115 | if (SUCCEEDED(hr)) | ||
| 116 | { | ||
| 117 | hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu", | ||
| 118 | (WORD)(qwValue >> 48), | ||
| 119 | (WORD)(qwValue >> 32), | ||
| 120 | (WORD)(qwValue >> 16), | ||
| 121 | (WORD)qwValue); | ||
| 122 | ExitOnFailure(hr, "Failed to convert version to string."); | ||
| 123 | } | ||
| 124 | SecureZeroMemory(&qwValue, sizeof(qwValue)); | ||
| 125 | break; | ||
| 126 | default: | ||
| 127 | hr = E_INVALIDARG; | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | |||
| 131 | LExit: | ||
| 132 | return hr; | ||
| 133 | } | ||
| 134 | |||
| 135 | // The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
| 136 | extern "C" HRESULT BVariantGetVersion( | ||
| 137 | __in BURN_VARIANT* pVariant, | ||
| 138 | __out DWORD64* pqwValue | ||
| 139 | ) | ||
| 140 | { | ||
| 141 | HRESULT hr = S_OK; | ||
| 142 | LPWSTR sczValue = NULL; | ||
| 143 | |||
| 144 | switch (pVariant->Type) | ||
| 145 | { | ||
| 146 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 147 | BVariantRetrieveDecryptedNumeric(pVariant, (LONGLONG*)pqwValue); | ||
| 148 | break; | ||
| 149 | case BURN_VARIANT_TYPE_STRING: | ||
| 150 | hr = BVariantRetrieveDecryptedString(pVariant, &sczValue); | ||
| 151 | if (SUCCEEDED(hr)) | ||
| 152 | { | ||
| 153 | hr = FileVersionFromStringEx(sczValue, 0, pqwValue); | ||
| 154 | if (FAILED(hr)) | ||
| 155 | { | ||
| 156 | hr = DISP_E_TYPEMISMATCH; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | StrSecureZeroFreeString(sczValue); | ||
| 160 | break; | ||
| 161 | case BURN_VARIANT_TYPE_VERSION: | ||
| 162 | BVariantRetrieveDecryptedVersion(pVariant, pqwValue); | ||
| 163 | break; | ||
| 164 | default: | ||
| 165 | hr = E_INVALIDARG; | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | |||
| 169 | return hr; | ||
| 170 | } | ||
| 171 | |||
| 172 | extern "C" HRESULT BVariantSetNumeric( | ||
| 173 | __in BURN_VARIANT* pVariant, | ||
| 174 | __in LONGLONG llValue | ||
| 175 | ) | ||
| 176 | { | ||
| 177 | HRESULT hr = S_OK; | ||
| 178 | BOOL fEncryptValue = pVariant->fEncryptValue; | ||
| 179 | |||
| 180 | if (BURN_VARIANT_TYPE_STRING == pVariant->Type) | ||
| 181 | { | ||
| 182 | StrSecureZeroFreeString(pVariant->sczValue); | ||
| 183 | } | ||
| 184 | memset(pVariant, 0, sizeof(BURN_VARIANT)); | ||
| 185 | pVariant->llValue = llValue; | ||
| 186 | pVariant->Type = BURN_VARIANT_TYPE_NUMERIC; | ||
| 187 | BVariantSetEncryption(pVariant, fEncryptValue); | ||
| 188 | |||
| 189 | return hr; | ||
| 190 | } | ||
| 191 | |||
| 192 | extern "C" HRESULT BVariantSetString( | ||
| 193 | __in BURN_VARIANT* pVariant, | ||
| 194 | __in_z_opt LPCWSTR wzValue, | ||
| 195 | __in DWORD_PTR cchValue | ||
| 196 | ) | ||
| 197 | { | ||
| 198 | HRESULT hr = S_OK; | ||
| 199 | BOOL fEncryptValue = pVariant->fEncryptValue; | ||
| 200 | |||
| 201 | if (!wzValue) // if we're nulling out the string, make the variable NONE. | ||
| 202 | { | ||
| 203 | BVariantUninitialize(pVariant); | ||
| 204 | } | ||
| 205 | else // assign the value. | ||
| 206 | { | ||
| 207 | if (BURN_VARIANT_TYPE_STRING != pVariant->Type) | ||
| 208 | { | ||
| 209 | memset(pVariant, 0, sizeof(BURN_VARIANT)); | ||
| 210 | } | ||
| 211 | else | ||
| 212 | { | ||
| 213 | // We're about to copy an unencrypted value. | ||
| 214 | pVariant->fEncryptValue = FALSE; | ||
| 215 | } | ||
| 216 | |||
| 217 | hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue); | ||
| 218 | ExitOnFailure(hr, "Failed to copy string."); | ||
| 219 | |||
| 220 | pVariant->Type = BURN_VARIANT_TYPE_STRING; | ||
| 221 | } | ||
| 222 | |||
| 223 | LExit: | ||
| 224 | BVariantSetEncryption(pVariant, fEncryptValue); | ||
| 225 | return hr; | ||
| 226 | } | ||
| 227 | |||
| 228 | extern "C" HRESULT BVariantSetVersion( | ||
| 229 | __in BURN_VARIANT* pVariant, | ||
| 230 | __in DWORD64 qwValue | ||
| 231 | ) | ||
| 232 | { | ||
| 233 | HRESULT hr = S_OK; | ||
| 234 | BOOL fEncryptValue = pVariant->fEncryptValue; | ||
| 235 | |||
| 236 | if (BURN_VARIANT_TYPE_STRING == pVariant->Type) | ||
| 237 | { | ||
| 238 | StrSecureZeroFreeString(pVariant->sczValue); | ||
| 239 | } | ||
| 240 | memset(pVariant, 0, sizeof(BURN_VARIANT)); | ||
| 241 | pVariant->qwValue = qwValue; | ||
| 242 | pVariant->Type = BURN_VARIANT_TYPE_VERSION; | ||
| 243 | BVariantSetEncryption(pVariant, fEncryptValue); | ||
| 244 | |||
| 245 | return hr; | ||
| 246 | } | ||
| 247 | |||
| 248 | extern "C" HRESULT BVariantSetValue( | ||
| 249 | __in BURN_VARIANT* pVariant, | ||
| 250 | __in BURN_VARIANT* pValue | ||
| 251 | ) | ||
| 252 | { | ||
| 253 | HRESULT hr = S_OK; | ||
| 254 | LONGLONG llValue = 0; | ||
| 255 | LPWSTR sczValue = NULL; | ||
| 256 | DWORD64 qwValue = 0; | ||
| 257 | BOOL fEncrypt = pVariant->fEncryptValue; | ||
| 258 | |||
| 259 | switch (pValue->Type) | ||
| 260 | { | ||
| 261 | case BURN_VARIANT_TYPE_NONE: | ||
| 262 | BVariantUninitialize(pVariant); | ||
| 263 | break; | ||
| 264 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 265 | hr = BVariantGetNumeric(pValue, &llValue); | ||
| 266 | if (SUCCEEDED(hr)) | ||
| 267 | { | ||
| 268 | hr = BVariantSetNumeric(pVariant, llValue); | ||
| 269 | } | ||
| 270 | SecureZeroMemory(&llValue, sizeof(llValue)); | ||
| 271 | break; | ||
| 272 | case BURN_VARIANT_TYPE_STRING: | ||
| 273 | hr = BVariantGetString(pValue, &sczValue); | ||
| 274 | if (SUCCEEDED(hr)) | ||
| 275 | { | ||
| 276 | hr = BVariantSetString(pVariant, sczValue, 0); | ||
| 277 | } | ||
| 278 | StrSecureZeroFreeString(sczValue); | ||
| 279 | break; | ||
| 280 | case BURN_VARIANT_TYPE_VERSION: | ||
| 281 | hr = BVariantGetVersion(pValue, &qwValue); | ||
| 282 | if (SUCCEEDED(hr)) | ||
| 283 | { | ||
| 284 | hr = BVariantSetVersion(pVariant, qwValue); | ||
| 285 | } | ||
| 286 | SecureZeroMemory(&qwValue, sizeof(qwValue)); | ||
| 287 | break; | ||
| 288 | default: | ||
| 289 | hr = E_INVALIDARG; | ||
| 290 | } | ||
| 291 | ExitOnFailure(hr, "Failed to copy variant."); | ||
| 292 | |||
| 293 | hr = BVariantSetEncryption(pVariant, fEncrypt); | ||
| 294 | |||
| 295 | LExit: | ||
| 296 | return hr; | ||
| 297 | } | ||
| 298 | |||
| 299 | extern "C" HRESULT BVariantCopy( | ||
| 300 | __in BURN_VARIANT* pSource, | ||
| 301 | __out BURN_VARIANT* pTarget | ||
| 302 | ) | ||
| 303 | { | ||
| 304 | HRESULT hr = S_OK; | ||
| 305 | LONGLONG llValue = 0; | ||
| 306 | LPWSTR sczValue = NULL; | ||
| 307 | DWORD64 qwValue = 0; | ||
| 308 | |||
| 309 | BVariantUninitialize(pTarget); | ||
| 310 | |||
| 311 | switch (pSource->Type) | ||
| 312 | { | ||
| 313 | case BURN_VARIANT_TYPE_NONE: | ||
| 314 | break; | ||
| 315 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 316 | hr = BVariantGetNumeric(pSource, &llValue); | ||
| 317 | if (SUCCEEDED(hr)) | ||
| 318 | { | ||
| 319 | hr = BVariantSetNumeric(pTarget, llValue); | ||
| 320 | } | ||
| 321 | SecureZeroMemory(&llValue, sizeof(llValue)); | ||
| 322 | break; | ||
| 323 | case BURN_VARIANT_TYPE_STRING: | ||
| 324 | hr = BVariantGetString(pSource, &sczValue); | ||
| 325 | if (SUCCEEDED(hr)) | ||
| 326 | { | ||
| 327 | hr = BVariantSetString(pTarget, sczValue, 0); | ||
| 328 | } | ||
| 329 | StrSecureZeroFreeString(sczValue); | ||
| 330 | break; | ||
| 331 | case BURN_VARIANT_TYPE_VERSION: | ||
| 332 | hr = BVariantGetVersion(pSource, &qwValue); | ||
| 333 | if (SUCCEEDED(hr)) | ||
| 334 | { | ||
| 335 | hr = BVariantSetVersion(pTarget, qwValue); | ||
| 336 | } | ||
| 337 | SecureZeroMemory(&qwValue, sizeof(qwValue)); | ||
| 338 | break; | ||
| 339 | default: | ||
| 340 | hr = E_INVALIDARG; | ||
| 341 | } | ||
| 342 | ExitOnFailure(hr, "Failed to copy variant."); | ||
| 343 | |||
| 344 | hr = BVariantSetEncryption(pTarget, pSource->fEncryptValue); | ||
| 345 | |||
| 346 | LExit: | ||
| 347 | return hr; | ||
| 348 | } | ||
| 349 | |||
| 350 | extern "C" HRESULT BVariantChangeType( | ||
| 351 | __in BURN_VARIANT* pVariant, | ||
| 352 | __in BURN_VARIANT_TYPE type | ||
| 353 | ) | ||
| 354 | { | ||
| 355 | HRESULT hr = S_OK; | ||
| 356 | BURN_VARIANT variant = { }; | ||
| 357 | BOOL fEncryptValue = pVariant->fEncryptValue; | ||
| 358 | |||
| 359 | if (pVariant->Type == type) | ||
| 360 | { | ||
| 361 | ExitFunction(); // variant already is of the requested type | ||
| 362 | } | ||
| 363 | |||
| 364 | switch (type) | ||
| 365 | { | ||
| 366 | case BURN_VARIANT_TYPE_NONE: | ||
| 367 | hr = S_OK; | ||
| 368 | break; | ||
| 369 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 370 | hr = BVariantGetNumeric(pVariant, &variant.llValue); | ||
| 371 | break; | ||
| 372 | case BURN_VARIANT_TYPE_STRING: | ||
| 373 | hr = BVariantGetString(pVariant, &variant.sczValue); | ||
| 374 | break; | ||
| 375 | case BURN_VARIANT_TYPE_VERSION: | ||
| 376 | hr = BVariantGetVersion(pVariant, &variant.qwValue); | ||
| 377 | break; | ||
| 378 | default: | ||
| 379 | ExitFunction1(hr = E_INVALIDARG); | ||
| 380 | } | ||
| 381 | ExitOnFailure(hr, "Failed to copy variant value."); | ||
| 382 | variant.Type = type; | ||
| 383 | |||
| 384 | BVariantUninitialize(pVariant); | ||
| 385 | memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT)); | ||
| 386 | SecureZeroMemory(&variant, sizeof(BURN_VARIANT)); | ||
| 387 | BVariantSetEncryption(pVariant, fEncryptValue); | ||
| 388 | |||
| 389 | LExit: | ||
| 390 | return hr; | ||
| 391 | } | ||
| 392 | |||
| 393 | extern "C" HRESULT BVariantSetEncryption( | ||
| 394 | __in BURN_VARIANT* pVariant, | ||
| 395 | __in BOOL fEncrypt | ||
| 396 | ) | ||
| 397 | { | ||
| 398 | HRESULT hr = S_OK; | ||
| 399 | |||
| 400 | if (pVariant->fEncryptValue == fEncrypt) | ||
| 401 | { | ||
| 402 | // The requested encryption state is already applied. | ||
| 403 | ExitFunction(); | ||
| 404 | } | ||
| 405 | |||
| 406 | switch (pVariant->Type) | ||
| 407 | { | ||
| 408 | case BURN_VARIANT_TYPE_NONE: | ||
| 409 | hr = S_OK; | ||
| 410 | break; | ||
| 411 | case BURN_VARIANT_TYPE_NUMERIC: | ||
| 412 | hr = BVariantEncryptNumeric(pVariant, fEncrypt); | ||
| 413 | break; | ||
| 414 | case BURN_VARIANT_TYPE_STRING: | ||
| 415 | hr = BVariantEncryptString(pVariant, fEncrypt); | ||
| 416 | break; | ||
| 417 | case BURN_VARIANT_TYPE_VERSION: | ||
| 418 | hr = BVariantEncryptVersion(pVariant, fEncrypt); | ||
| 419 | break; | ||
| 420 | default: | ||
| 421 | hr = E_INVALIDARG; | ||
| 422 | } | ||
| 423 | ExitOnFailure(hr, "Failed to set the variant's encryption state"); | ||
| 424 | pVariant->fEncryptValue = fEncrypt; | ||
| 425 | |||
| 426 | LExit: | ||
| 427 | return hr; | ||
| 428 | } | ||
| 429 | |||
| 430 | static HRESULT BVariantEncryptNumeric( | ||
| 431 | __in BURN_VARIANT* pVariant, | ||
| 432 | __in BOOL fEncrypt | ||
| 433 | ) | ||
| 434 | { | ||
| 435 | HRESULT hr = S_OK; | ||
| 436 | |||
| 437 | if (fEncrypt) | ||
| 438 | { | ||
| 439 | hr = CrypEncryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); | ||
| 440 | } | ||
| 441 | else | ||
| 442 | { | ||
| 443 | hr = CrypDecryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); | ||
| 444 | } | ||
| 445 | |||
| 446 | //LExit: | ||
| 447 | return hr; | ||
| 448 | } | ||
| 449 | |||
| 450 | static HRESULT BVariantEncryptString( | ||
| 451 | __in BURN_VARIANT* pVariant, | ||
| 452 | __in BOOL fEncrypt | ||
| 453 | ) | ||
| 454 | { | ||
| 455 | HRESULT hr = S_OK; | ||
| 456 | SIZE_T cbData = 0; | ||
| 457 | |||
| 458 | if (NULL == pVariant->sczValue) | ||
| 459 | { | ||
| 460 | ExitFunction(); | ||
| 461 | } | ||
| 462 | |||
| 463 | cbData = MemSize(pVariant->sczValue); | ||
| 464 | if (-1 == cbData) | ||
| 465 | { | ||
| 466 | hr = E_INVALIDARG; | ||
| 467 | ExitOnFailure(hr, "Failed to get the size of the string"); | ||
| 468 | } | ||
| 469 | |||
| 470 | DWORD remainder = fEncrypt ? cbData % CRYP_ENCRYPT_MEMORY_SIZE : 0; | ||
| 471 | DWORD extraNeeded = 0 < remainder ? CRYP_ENCRYPT_MEMORY_SIZE - remainder : 0; | ||
| 472 | if ((MAXDWORD - extraNeeded) < cbData) | ||
| 473 | { | ||
| 474 | hr = E_INVALIDDATA; | ||
| 475 | ExitOnFailure(hr, "The string is too big: size %u", cbData); | ||
| 476 | } | ||
| 477 | else if (0 < extraNeeded) | ||
| 478 | { | ||
| 479 | cbData += extraNeeded; | ||
| 480 | LPVOID pvNew = NULL; | ||
| 481 | hr = MemReAllocSecure(static_cast<LPVOID>(pVariant->sczValue), cbData, TRUE, &pvNew); | ||
| 482 | ExitOnFailure(hr, "Failed to resize the string so it could be encrypted"); | ||
| 483 | pVariant->sczValue = static_cast<LPWSTR>(pvNew); | ||
| 484 | } | ||
| 485 | |||
| 486 | if (fEncrypt) | ||
| 487 | { | ||
| 488 | hr = CrypEncryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE); | ||
| 489 | } | ||
| 490 | else | ||
| 491 | { | ||
| 492 | hr = CrypDecryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE); | ||
| 493 | } | ||
| 494 | |||
| 495 | LExit: | ||
| 496 | return hr; | ||
| 497 | } | ||
| 498 | |||
| 499 | static HRESULT BVariantEncryptVersion( | ||
| 500 | __in BURN_VARIANT* pVariant, | ||
| 501 | __in BOOL fEncrypt | ||
| 502 | ) | ||
| 503 | { | ||
| 504 | HRESULT hr = S_OK; | ||
| 505 | |||
| 506 | if (fEncrypt) | ||
| 507 | { | ||
| 508 | hr = CrypEncryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); | ||
| 509 | } | ||
| 510 | else | ||
| 511 | { | ||
| 512 | hr = CrypDecryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE); | ||
| 513 | } | ||
| 514 | |||
| 515 | //LExit: | ||
| 516 | return hr; | ||
| 517 | } | ||
| 518 | |||
| 519 | // The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
| 520 | static HRESULT BVariantRetrieveDecryptedNumeric( | ||
| 521 | __in BURN_VARIANT* pVariant, | ||
| 522 | __out LONGLONG* pllValue | ||
| 523 | ) | ||
| 524 | { | ||
| 525 | HRESULT hr = S_OK; | ||
| 526 | |||
| 527 | Assert(NULL != pllValue); | ||
| 528 | if (pVariant->fEncryptValue) | ||
| 529 | { | ||
| 530 | hr = BVariantEncryptNumeric(pVariant, FALSE); | ||
| 531 | ExitOnFailure(hr, "Failed to decrypt numeric"); | ||
| 532 | } | ||
| 533 | |||
| 534 | *pllValue = pVariant->llValue; | ||
| 535 | |||
| 536 | if (pVariant->fEncryptValue) | ||
| 537 | { | ||
| 538 | hr = BVariantEncryptNumeric(pVariant, TRUE); | ||
| 539 | } | ||
| 540 | |||
| 541 | LExit: | ||
| 542 | return hr; | ||
| 543 | } | ||
| 544 | |||
| 545 | // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. | ||
| 546 | static HRESULT BVariantRetrieveDecryptedString( | ||
| 547 | __in BURN_VARIANT* pVariant, | ||
| 548 | __out LPWSTR* psczValue | ||
| 549 | ) | ||
| 550 | { | ||
| 551 | HRESULT hr = S_OK; | ||
| 552 | |||
| 553 | if (NULL == pVariant->sczValue) | ||
| 554 | { | ||
| 555 | *psczValue = NULL; | ||
| 556 | ExitFunction(); | ||
| 557 | } | ||
| 558 | |||
| 559 | if (pVariant->fEncryptValue) | ||
| 560 | { | ||
| 561 | hr = BVariantEncryptString(pVariant, FALSE); | ||
| 562 | ExitOnFailure(hr, "Failed to decrypt string"); | ||
| 563 | } | ||
| 564 | |||
| 565 | hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0); | ||
| 566 | ExitOnFailure(hr, "Failed to copy value."); | ||
| 567 | |||
| 568 | if (pVariant->fEncryptValue) | ||
| 569 | { | ||
| 570 | hr = BVariantEncryptString(pVariant, TRUE); | ||
| 571 | } | ||
| 572 | |||
| 573 | LExit: | ||
| 574 | return hr; | ||
| 575 | } | ||
| 576 | |||
| 577 | // The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
| 578 | static HRESULT BVariantRetrieveDecryptedVersion( | ||
| 579 | __in BURN_VARIANT* pVariant, | ||
| 580 | __out DWORD64* pqwValue | ||
| 581 | ) | ||
| 582 | { | ||
| 583 | HRESULT hr = S_OK; | ||
| 584 | |||
| 585 | Assert(NULL != pqwValue); | ||
| 586 | if (pVariant->fEncryptValue) | ||
| 587 | { | ||
| 588 | hr = BVariantEncryptVersion(pVariant, FALSE); | ||
| 589 | ExitOnFailure(hr, "Failed to decrypt version"); | ||
| 590 | } | ||
| 591 | |||
| 592 | *pqwValue = pVariant->qwValue; | ||
| 593 | |||
| 594 | if (pVariant->fEncryptValue) | ||
| 595 | { | ||
| 596 | hr = BVariantEncryptVersion(pVariant, TRUE); | ||
| 597 | } | ||
| 598 | |||
| 599 | LExit: | ||
| 600 | return hr; | ||
| 601 | } | ||
diff --git a/src/engine/variant.h b/src/engine/variant.h new file mode 100644 index 00000000..9259f05a --- /dev/null +++ b/src/engine/variant.h | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #pragma once | ||
| 2 | // 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. | ||
| 3 | |||
| 4 | |||
| 5 | #if defined(__cplusplus) | ||
| 6 | extern "C" { | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | // constants | ||
| 11 | |||
| 12 | enum BURN_VARIANT_TYPE | ||
| 13 | { | ||
| 14 | BURN_VARIANT_TYPE_NONE, | ||
| 15 | BURN_VARIANT_TYPE_NUMERIC, | ||
| 16 | BURN_VARIANT_TYPE_STRING, | ||
| 17 | BURN_VARIANT_TYPE_VERSION, | ||
| 18 | }; | ||
| 19 | |||
| 20 | |||
| 21 | // struct | ||
| 22 | |||
| 23 | typedef struct _BURN_VARIANT | ||
| 24 | { | ||
| 25 | union | ||
| 26 | { | ||
| 27 | LONGLONG llValue; | ||
| 28 | DWORD64 qwValue; | ||
| 29 | LPWSTR sczValue; | ||
| 30 | BYTE encryptionPadding[CRYP_ENCRYPT_MEMORY_SIZE]; | ||
| 31 | }; | ||
| 32 | BURN_VARIANT_TYPE Type; | ||
| 33 | BOOL fEncryptValue; | ||
| 34 | } BURN_VARIANT; | ||
| 35 | |||
| 36 | |||
| 37 | // function declarations | ||
| 38 | |||
| 39 | void BVariantUninitialize( | ||
| 40 | __in BURN_VARIANT* pVariant | ||
| 41 | ); | ||
| 42 | HRESULT BVariantGetNumeric( | ||
| 43 | __in BURN_VARIANT* pVariant, | ||
| 44 | __out LONGLONG* pllValue | ||
| 45 | ); | ||
| 46 | HRESULT BVariantGetString( | ||
| 47 | __in BURN_VARIANT* pVariant, | ||
| 48 | __out_z LPWSTR* psczValue | ||
| 49 | ); | ||
| 50 | HRESULT BVariantGetVersion( | ||
| 51 | __in BURN_VARIANT* pVariant, | ||
| 52 | __out DWORD64* pqwValue | ||
| 53 | ); | ||
| 54 | HRESULT BVariantSetNumeric( | ||
| 55 | __in BURN_VARIANT* pVariant, | ||
| 56 | __in LONGLONG llValue | ||
| 57 | ); | ||
| 58 | HRESULT BVariantSetString( | ||
| 59 | __in BURN_VARIANT* pVariant, | ||
| 60 | __in_z_opt LPCWSTR wzValue, | ||
| 61 | __in DWORD_PTR cchValue | ||
| 62 | ); | ||
| 63 | HRESULT BVariantSetVersion( | ||
| 64 | __in BURN_VARIANT* pVariant, | ||
| 65 | __in DWORD64 qwValue | ||
| 66 | ); | ||
| 67 | /******************************************************************** | ||
| 68 | BVariantSetValue - Convenience function that calls BVariantUninitialize, | ||
| 69 | BVariantSetNumeric, BVariantSetString, or | ||
| 70 | BVariantSetVersion based on the type of pValue. | ||
| 71 | The encryption state of pVariant is preserved. | ||
| 72 | ********************************************************************/ | ||
| 73 | HRESULT BVariantSetValue( | ||
| 74 | __in BURN_VARIANT* pVariant, | ||
| 75 | __in BURN_VARIANT* pValue | ||
| 76 | ); | ||
| 77 | /******************************************************************** | ||
| 78 | BVariantCopy - creates a copy of pSource. | ||
| 79 | The encryption state of pTarget is set to | ||
| 80 | the encryption state of pSource. | ||
| 81 | ********************************************************************/ | ||
| 82 | HRESULT BVariantCopy( | ||
| 83 | __in BURN_VARIANT* pSource, | ||
| 84 | __out BURN_VARIANT* pTarget | ||
| 85 | ); | ||
| 86 | HRESULT BVariantChangeType( | ||
| 87 | __in BURN_VARIANT* pVariant, | ||
| 88 | __in BURN_VARIANT_TYPE type | ||
| 89 | ); | ||
| 90 | /******************************************************************** | ||
| 91 | BVariantSetEncryption - sets the encryption state of pVariant. | ||
| 92 | If the encryption state matches the requested | ||
| 93 | state, this function does nothing. | ||
| 94 | ********************************************************************/ | ||
| 95 | HRESULT BVariantSetEncryption( | ||
| 96 | __in BURN_VARIANT* pVariant, | ||
| 97 | __in BOOL fEncrypt | ||
| 98 | ); | ||
| 99 | |||
| 100 | #if defined(__cplusplus) | ||
| 101 | } | ||
| 102 | #endif | ||
