aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/cache.cpp')
-rw-r--r--src/burn/engine/cache.cpp2052
1 files changed, 2052 insertions, 0 deletions
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
new file mode 100644
index 00000000..59daf139
--- /dev/null
+++ b/src/burn/engine/cache.cpp
@@ -0,0 +1,2052 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr";
6static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be";
7static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified";
8static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache";
9static const DWORD FILE_OPERATION_RETRY_COUNT = 3;
10static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
11
12static BOOL vfInitializedCache = FALSE;
13static BOOL vfRunningFromCache = FALSE;
14static LPWSTR vsczSourceProcessFolder = NULL;
15static LPWSTR vsczWorkingFolder = NULL;
16static LPWSTR vsczDefaultUserPackageCache = NULL;
17static LPWSTR vsczDefaultMachinePackageCache = NULL;
18static LPWSTR vsczCurrentMachinePackageCache = NULL;
19
20static HRESULT CalculateWorkingFolder(
21 __in_z LPCWSTR wzBundleId,
22 __deref_out_z LPWSTR* psczWorkingFolder
23 );
24static HRESULT GetLastUsedSourceFolder(
25 __in BURN_VARIABLES* pVariables,
26 __out_z LPWSTR* psczLastSource
27 );
28static HRESULT CreateCompletedPath(
29 __in BOOL fPerMachine,
30 __in LPCWSTR wzCacheId,
31 __out LPWSTR* psczCacheDirectory
32 );
33static HRESULT CreateUnverifiedPath(
34 __in BOOL fPerMachine,
35 __in_z LPCWSTR wzPayloadId,
36 __out_z LPWSTR* psczUnverifiedPayloadPath
37 );
38static HRESULT GetRootPath(
39 __in BOOL fPerMachine,
40 __in BOOL fAllowRedirect,
41 __deref_out_z LPWSTR* psczRootPath
42 );
43static HRESULT VerifyThenTransferContainer(
44 __in BURN_CONTAINER* pContainer,
45 __in_z LPCWSTR wzCachedPath,
46 __in_z LPCWSTR wzUnverifiedContainerPath,
47 __in BOOL fMove,
48 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
49 __in LPPROGRESS_ROUTINE pfnProgress,
50 __in LPVOID pContext
51 );
52static HRESULT VerifyThenTransferPayload(
53 __in BURN_PAYLOAD* pPayload,
54 __in_z LPCWSTR wzCachedPath,
55 __in_z LPCWSTR wzUnverifiedPayloadPath,
56 __in BOOL fMove,
57 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
58 __in LPPROGRESS_ROUTINE pfnProgress,
59 __in LPVOID pContext
60 );
61static HRESULT CacheTransferFileWithRetry(
62 __in_z LPCWSTR wzSourcePath,
63 __in_z LPCWSTR wzDestinationPath,
64 __in BOOL fMove,
65 __in BURN_CACHE_STEP cacheStep,
66 __in DWORD64 qwFileSize,
67 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
68 __in LPPROGRESS_ROUTINE pfnProgress,
69 __in LPVOID pContext
70 );
71static HRESULT VerifyFileAgainstContainer(
72 __in BURN_CONTAINER* pContainer,
73 __in_z LPCWSTR wzVerifyPath,
74 __in BOOL fAlreadyCached,
75 __in BURN_CACHE_STEP cacheStep,
76 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
77 __in LPPROGRESS_ROUTINE pfnProgress,
78 __in LPVOID pContext
79 );
80static HRESULT VerifyFileAgainstPayload(
81 __in BURN_PAYLOAD* pPayload,
82 __in_z LPCWSTR wzVerifyPath,
83 __in BOOL fAlreadyCached,
84 __in BURN_CACHE_STEP cacheStep,
85 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
86 __in LPPROGRESS_ROUTINE pfnProgress,
87 __in LPVOID pContext
88 );
89static HRESULT ResetPathPermissions(
90 __in BOOL fPerMachine,
91 __in_z LPCWSTR wzPath
92 );
93static HRESULT SecurePath(
94 __in LPCWSTR wzPath
95 );
96static HRESULT CopyEngineToWorkingFolder(
97 __in_z LPCWSTR wzSourcePath,
98 __in_z LPCWSTR wzWorkingFolderName,
99 __in_z LPCWSTR wzExecutableName,
100 __in BURN_SECTION* pSection,
101 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
102 );
103static HRESULT CopyEngineWithSignatureFixup(
104 __in HANDLE hEngineFile,
105 __in_z LPCWSTR wzEnginePath,
106 __in_z LPCWSTR wzTargetPath,
107 __in BURN_SECTION* pSection
108 );
109static HRESULT RemoveBundleOrPackage(
110 __in BOOL fBundle,
111 __in BOOL fPerMachine,
112 __in_z LPCWSTR wzBundleOrPackageId,
113 __in_z LPCWSTR wzCacheId
114 );
115static HRESULT VerifyHash(
116 __in BYTE* pbHash,
117 __in DWORD cbHash,
118 __in DWORD64 qwFileSize,
119 __in_z LPCWSTR wzUnverifiedPayloadPath,
120 __in HANDLE hFile,
121 __in BURN_CACHE_STEP cacheStep,
122 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
123 __in LPPROGRESS_ROUTINE pfnProgress,
124 __in LPVOID pContext
125 );
126static HRESULT SendCacheBeginMessage(
127 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
128 __in LPVOID pContext,
129 __in BURN_CACHE_STEP cacheStep
130 );
131static HRESULT SendCacheSuccessMessage(
132 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
133 __in LPVOID pContext,
134 __in DWORD64 qwFileSize
135 );
136static HRESULT SendCacheCompleteMessage(
137 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
138 __in LPVOID pContext,
139 __in HRESULT hrStatus
140 );
141
142
143extern "C" HRESULT CacheInitialize(
144 __in BURN_REGISTRATION* pRegistration,
145 __in BURN_VARIABLES* pVariables,
146 __in_z_opt LPCWSTR wzSourceProcessPath
147 )
148{
149 HRESULT hr = S_OK;
150 LPWSTR sczCurrentPath = NULL;
151 LPWSTR sczCompletedFolder = NULL;
152 LPWSTR sczCompletedPath = NULL;
153 LPWSTR sczOriginalSource = NULL;
154 LPWSTR sczOriginalSourceFolder = NULL;
155 int nCompare = 0;
156
157 if (!vfInitializedCache)
158 {
159 hr = PathForCurrentProcess(&sczCurrentPath, NULL);
160 ExitOnFailure(hr, "Failed to get current process path.");
161
162 // Determine if we are running from the package cache or not.
163 hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder);
164 ExitOnFailure(hr, "Failed to get completed path for bundle.");
165
166 hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
167 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
168
169 hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare);
170 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
171
172 vfRunningFromCache = (CSTR_EQUAL == nCompare);
173
174 // If a source process path was not provided (e.g. we are not being
175 // run in a clean room) then use the current process path as the
176 // source process path.
177 if (!wzSourceProcessPath)
178 {
179 wzSourceProcessPath = sczCurrentPath;
180 }
181
182 hr = PathGetDirectory(wzSourceProcessPath, &vsczSourceProcessFolder);
183 ExitOnFailure(hr, "Failed to initialize cache source folder.");
184
185 // If we're not running from the cache, ensure the original source is set.
186 if (!vfRunningFromCache)
187 {
188 // If the original source has not been set already then set it where the bundle is
189 // running from right now. This value will be persisted and we'll use it when launched
190 // from the clean room or package cache since none of our packages will be relative to
191 // those locations.
192 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
193 if (E_NOTFOUND == hr)
194 {
195 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE, FALSE);
196 ExitOnFailure(hr, "Failed to set original source variable.");
197
198 hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0);
199 ExitOnFailure(hr, "Failed to copy current path to original source.");
200 }
201
202 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder);
203 if (E_NOTFOUND == hr)
204 {
205 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
206 ExitOnFailure(hr, "Failed to get directory from original source path.");
207
208 hr = VariableSetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE, FALSE);
209 ExitOnFailure(hr, "Failed to set original source directory variable.");
210 }
211 }
212
213 vfInitializedCache = TRUE;
214 }
215
216LExit:
217 ReleaseStr(sczCurrentPath);
218 ReleaseStr(sczCompletedFolder);
219 ReleaseStr(sczCompletedPath);
220 ReleaseStr(sczOriginalSource);
221 ReleaseStr(sczOriginalSourceFolder);
222
223 return hr;
224}
225
226extern "C" HRESULT CacheEnsureWorkingFolder(
227 __in_z_opt LPCWSTR wzBundleId,
228 __deref_out_z_opt LPWSTR* psczWorkingFolder
229 )
230{
231 HRESULT hr = S_OK;
232 LPWSTR sczWorkingFolder = NULL;
233
234 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
235 ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists.");
236
237 hr = DirEnsureExists(sczWorkingFolder, NULL);
238 ExitOnFailure(hr, "Failed create working folder.");
239
240 // Best effort to ensure our working folder is not encrypted.
241 ::DecryptFileW(sczWorkingFolder, 0);
242
243 if (psczWorkingFolder)
244 {
245 hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0);
246 ExitOnFailure(hr, "Failed to copy working folder.");
247 }
248
249LExit:
250 ReleaseStr(sczWorkingFolder);
251
252 return hr;
253}
254
255extern "C" HRESULT CacheCalculateBundleWorkingPath(
256 __in_z LPCWSTR wzBundleId,
257 __in LPCWSTR wzExecutableName,
258 __deref_out_z LPWSTR* psczWorkingPath
259 )
260{
261 Assert(vfInitializedCache);
262
263 HRESULT hr = S_OK;
264 LPWSTR sczWorkingFolder = NULL;
265
266 // If the bundle is running out of the package cache then we use that as the
267 // working folder since we feel safe in the package cache.
268 if (vfRunningFromCache)
269 {
270 hr = PathForCurrentProcess(psczWorkingPath, NULL);
271 ExitOnFailure(hr, "Failed to get current process path.");
272 }
273 else // Otherwise, use the real working folder.
274 {
275 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
276 ExitOnFailure(hr, "Failed to get working folder for bundle.");
277
278 hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName);
279 ExitOnFailure(hr, "Failed to calculate the bundle working path.");
280 }
281
282LExit:
283 ReleaseStr(sczWorkingFolder);
284
285 return hr;
286}
287
288extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath(
289 __in_z LPCWSTR wzBundleId,
290 __deref_out_z LPWSTR* psczWorkingPath
291 )
292{
293 HRESULT hr = S_OK;
294 LPWSTR sczWorkingFolder = NULL;
295
296 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
297 ExitOnFailure(hr, "Failed to get working folder for bundle layout.");
298
299 hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0);
300 ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path.");
301
302LExit:
303 ReleaseStr(sczWorkingFolder);
304
305 return hr;
306}
307
308extern "C" HRESULT CacheCalculatePayloadWorkingPath(
309 __in_z LPCWSTR wzBundleId,
310 __in BURN_PAYLOAD* pPayload,
311 __deref_out_z LPWSTR* psczWorkingPath
312 )
313{
314 HRESULT hr = S_OK;
315
316 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
317 ExitOnFailure(hr, "Failed to get working folder for payload.");
318
319 hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0);
320 ExitOnFailure(hr, "Failed to append Id as payload unverified path.");
321
322LExit:
323 return hr;
324}
325
326extern "C" HRESULT CacheCalculateContainerWorkingPath(
327 __in_z LPCWSTR wzBundleId,
328 __in BURN_CONTAINER* pContainer,
329 __deref_out_z LPWSTR* psczWorkingPath
330 )
331{
332 HRESULT hr = S_OK;
333
334 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
335 ExitOnFailure(hr, "Failed to get working folder for container.");
336
337 hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0);
338 ExitOnFailure(hr, "Failed to append hash as container unverified path.");
339
340LExit:
341 return hr;
342}
343
344extern "C" HRESULT CacheGetRootCompletedPath(
345 __in BOOL fPerMachine,
346 __in BOOL fForceInitialize,
347 __deref_out_z LPWSTR* psczRootCompletedPath
348 )
349{
350 HRESULT hr = S_OK;
351
352 if (fForceInitialize)
353 {
354 hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath);
355 }
356 else
357 {
358 hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath);
359 }
360
361 return hr;
362}
363
364extern "C" HRESULT CacheGetCompletedPath(
365 __in BOOL fPerMachine,
366 __in_z LPCWSTR wzCacheId,
367 __deref_out_z LPWSTR* psczCompletedPath
368 )
369{
370 HRESULT hr = S_OK;
371 BOOL fRedirected = FALSE;
372 LPWSTR sczRootPath = NULL;
373 LPWSTR sczCurrentCompletedPath = NULL;
374 LPWSTR sczDefaultCompletedPath = NULL;
375
376 hr = GetRootPath(fPerMachine, TRUE, &sczRootPath);
377 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
378
379 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
380 fRedirected = S_FALSE == hr;
381
382 hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath);
383 ExitOnFailure(hr, "Failed to construct cache path.");
384
385 hr = PathBackslashTerminate(&sczCurrentCompletedPath);
386 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
387
388 // Return the old package cache directory if the new directory does not exist but the old directory does.
389 // If neither package cache directory exists return the (possibly) redirected package cache directory.
390 if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL))
391 {
392 hr = GetRootPath(fPerMachine, FALSE, &sczRootPath);
393 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
394
395 hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath);
396 ExitOnFailure(hr, "Failed to construct cache path.");
397
398 hr = PathBackslashTerminate(&sczDefaultCompletedPath);
399 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
400
401 if (DirExists(sczDefaultCompletedPath, NULL))
402 {
403 *psczCompletedPath = sczDefaultCompletedPath;
404 sczDefaultCompletedPath = NULL;
405
406 ExitFunction();
407 }
408 }
409
410 *psczCompletedPath = sczCurrentCompletedPath;
411 sczCurrentCompletedPath = NULL;
412
413LExit:
414 ReleaseNullStr(sczDefaultCompletedPath);
415 ReleaseNullStr(sczCurrentCompletedPath);
416 ReleaseNullStr(sczRootPath);
417
418 return hr;
419}
420
421extern "C" HRESULT CacheGetResumePath(
422 __in_z LPCWSTR wzPayloadWorkingPath,
423 __deref_out_z LPWSTR* psczResumePath
424 )
425{
426 HRESULT hr = S_OK;
427
428 hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
429 ExitOnFailure(hr, "Failed to create resume path.");
430
431LExit:
432 return hr;
433}
434
435extern "C" HRESULT CacheGetLocalSourcePaths(
436 __in_z LPCWSTR wzRelativePath,
437 __in_z LPCWSTR wzSourcePath,
438 __in_z LPCWSTR wzDestinationPath,
439 __in_z_opt LPCWSTR wzLayoutDirectory,
440 __in BURN_VARIABLES* pVariables,
441 __inout LPWSTR** prgSearchPaths,
442 __out DWORD* pcSearchPaths,
443 __out DWORD* pdwLikelySearchPath,
444 __out DWORD* pdwDestinationSearchPath
445 )
446{
447 HRESULT hr = S_OK;
448 LPWSTR sczCurrentPath = NULL;
449 LPWSTR sczLastSourceFolder = NULL;
450 LPWSTR* psczPath = NULL;
451 BOOL fPreferSourcePathLocation = FALSE;
452 BOOL fTryLastFolder = FALSE;
453 BOOL fTryRelativePath = FALSE;
454 BOOL fSourceIsAbsolute = FALSE;
455 DWORD cSearchPaths = 0;
456 DWORD dwLikelySearchPath = 0;
457 DWORD dwDestinationSearchPath = 0;
458
459 AssertSz(vfInitializedCache, "Cache wasn't initialized");
460
461 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
462 fPreferSourcePathLocation = !vfRunningFromCache || FAILED(hr);
463 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, vsczSourceProcessFolder, -1, sczLastSourceFolder, -1);
464 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1);
465 fSourceIsAbsolute = PathIsAbsolute(wzSourcePath);
466
467 // If the source path provided is a full path, try that first.
468 if (fSourceIsAbsolute)
469 {
470 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
471 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
472
473 psczPath = *prgSearchPaths + cSearchPaths;
474 ++cSearchPaths;
475
476 hr = StrAllocString(psczPath, wzSourcePath, 0);
477 ExitOnFailure(hr, "Failed to copy absolute source path.");
478 }
479 else
480 {
481 // If none of the paths exist, then most BAs will want to prompt the user with a possible path.
482 // The destination path is a temporary location and so not really a possible path.
483 dwLikelySearchPath = 1;
484 }
485
486 // Try the destination path next.
487 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
488 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
489
490 dwDestinationSearchPath = cSearchPaths;
491 psczPath = *prgSearchPaths + cSearchPaths;
492 ++cSearchPaths;
493
494 hr = StrAllocString(psczPath, wzDestinationPath, 0);
495 ExitOnFailure(hr, "Failed to copy absolute source path.");
496
497 if (!fSourceIsAbsolute)
498 {
499 // Calculate the source path location.
500 // In the case where we are in the bundle's package cache and
501 // couldn't find a last used source that will be the package cache path
502 // which isn't likely to have what we are looking for.
503 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
504 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
505
506 hr = PathConcat(vsczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
507 ExitOnFailure(hr, "Failed to combine source process folder with source.");
508
509 // If we're not running from cache or we couldn't get the last source,
510 // try the source path location next.
511 if (fPreferSourcePathLocation)
512 {
513 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
514 ++cSearchPaths;
515 sczCurrentPath = NULL;
516 }
517
518 // If we have a last used source and it is not the source path location,
519 // add the last used source to the search path next.
520 if (fTryLastFolder)
521 {
522 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
523 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
524
525 psczPath = *prgSearchPaths + cSearchPaths;
526 ++cSearchPaths;
527
528 hr = PathConcat(sczLastSourceFolder, wzSourcePath, psczPath);
529 ExitOnFailure(hr, "Failed to combine last source with source.");
530 }
531
532 if (!fPreferSourcePathLocation)
533 {
534 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
535 ++cSearchPaths;
536 sczCurrentPath = NULL;
537 }
538
539 // Also consider the layout directory if doing Layout.
540 if (wzLayoutDirectory)
541 {
542 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
543 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
544
545 psczPath = *prgSearchPaths + cSearchPaths;
546 ++cSearchPaths;
547
548 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
549 ExitOnFailure(hr, "Failed to combine layout source with source.");
550 }
551 }
552
553 if (fTryRelativePath)
554 {
555 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
556 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
557
558 hr = PathConcat(vsczSourceProcessFolder, wzRelativePath, &sczCurrentPath);
559 ExitOnFailure(hr, "Failed to combine source process folder with relative.");
560
561 if (fPreferSourcePathLocation)
562 {
563 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
564 ++cSearchPaths;
565 sczCurrentPath = NULL;
566 }
567
568 if (fTryLastFolder)
569 {
570 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
571 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
572
573 psczPath = *prgSearchPaths + cSearchPaths;
574 ++cSearchPaths;
575
576 hr = PathConcat(sczLastSourceFolder, wzRelativePath, psczPath);
577 ExitOnFailure(hr, "Failed to combine last source with relative.");
578 }
579
580 if (!fPreferSourcePathLocation)
581 {
582 (*prgSearchPaths)[cSearchPaths] = sczCurrentPath;
583 ++cSearchPaths;
584 sczCurrentPath = NULL;
585 }
586
587 if (wzLayoutDirectory)
588 {
589 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgSearchPaths), cSearchPaths + 1, sizeof(LPWSTR), BURN_CACHE_MAX_SEARCH_PATHS);
590 ExitOnFailure(hr, "Failed to ensure size for search paths array.");
591
592 psczPath = *prgSearchPaths + cSearchPaths;
593 ++cSearchPaths;
594
595 hr = PathConcat(wzLayoutDirectory, wzSourcePath, psczPath);
596 ExitOnFailure(hr, "Failed to combine layout source with relative.");
597 }
598 }
599
600LExit:
601 ReleaseStr(sczCurrentPath);
602 ReleaseStr(sczLastSourceFolder);
603
604 AssertSz(cSearchPaths <= BURN_CACHE_MAX_SEARCH_PATHS, "Got more than BURN_CACHE_MAX_SEARCH_PATHS search paths");
605 *pcSearchPaths = cSearchPaths;
606 *pdwLikelySearchPath = dwLikelySearchPath;
607 *pdwDestinationSearchPath = dwDestinationSearchPath;
608
609 return hr;
610}
611
612extern "C" HRESULT CacheSetLastUsedSource(
613 __in BURN_VARIABLES* pVariables,
614 __in_z LPCWSTR wzSourcePath,
615 __in_z LPCWSTR wzRelativePath
616 )
617{
618 HRESULT hr = S_OK;
619 size_t cchSourcePath = 0;
620 size_t cchRelativePath = 0;
621 size_t iSourceRelativePath = 0;
622 LPWSTR sczSourceFolder = NULL;
623 LPWSTR sczLastSourceFolder = NULL;
624 int nCompare = 0;
625
626 hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath);
627 ExitOnFailure(hr, "Failed to determine length of source path.");
628
629 hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath);
630 ExitOnFailure(hr, "Failed to determine length of relative path.");
631
632 // If the source path is smaller than the relative path (plus space for "X:\") then we know they
633 // are not relative to each other.
634 if (cchSourcePath < cchRelativePath + 3)
635 {
636 ExitFunction();
637 }
638
639 // If the source path ends with the relative path then this source could be a new path.
640 iSourceRelativePath = cchSourcePath - cchRelativePath;
641 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1))
642 {
643 hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath);
644 ExitOnFailure(hr, "Failed to trim source folder.");
645
646 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder);
647 if (SUCCEEDED(hr))
648 {
649 nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1);
650 }
651 else if (E_NOTFOUND == hr)
652 {
653 nCompare = CSTR_GREATER_THAN;
654 hr = S_OK;
655 }
656
657 if (CSTR_EQUAL != nCompare)
658 {
659 hr = VariableSetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE, FALSE);
660 ExitOnFailure(hr, "Failed to set last source.");
661 }
662 }
663
664LExit:
665 ReleaseStr(sczLastSourceFolder);
666 ReleaseStr(sczSourceFolder);
667
668 return hr;
669}
670
671extern "C" HRESULT CacheSendProgressCallback(
672 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
673 __in DWORD64 dw64Progress,
674 __in DWORD64 dw64Total,
675 __in HANDLE hDestinationFile
676 )
677{
678 static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
679
680 HRESULT hr = S_OK;
681 DWORD dwResult = PROGRESS_CONTINUE;
682 LARGE_INTEGER liTotalSize = { };
683 LARGE_INTEGER liTotalTransferred = { };
684
685 if (pCallback->pfnProgress)
686 {
687 liTotalSize.QuadPart = dw64Total;
688 liTotalTransferred.QuadPart = dw64Progress;
689
690 dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
691 switch (dwResult)
692 {
693 case PROGRESS_CONTINUE:
694 hr = S_OK;
695 break;
696
697 case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
698 case PROGRESS_STOP:
699 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
700 ExitOnRootFailure(hr, "UX aborted on download progress.");
701
702 case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
703 pCallback->pfnProgress = NULL;
704 hr = S_OK;
705 break;
706
707 default:
708 hr = E_UNEXPECTED;
709 ExitOnRootFailure(hr, "Invalid return code from progress routine.");
710 }
711 }
712
713LExit:
714 return hr;
715}
716
717extern "C" void CacheSendErrorCallback(
718 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
719 __in HRESULT hrError,
720 __in_z_opt LPCWSTR wzError,
721 __out_opt BOOL* pfRetry
722 )
723{
724 if (pfRetry)
725 {
726 *pfRetry = FALSE;
727 }
728
729 if (pCallback->pfnCancel)
730 {
731 int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv);
732 if (pfRetry && IDRETRY == nResult)
733 {
734 *pfRetry = TRUE;
735 }
736 }
737}
738
739extern "C" BOOL CacheBundleRunningFromCache()
740{
741 return vfRunningFromCache;
742}
743
744extern "C" HRESULT CacheBundleToCleanRoom(
745 __in BURN_SECTION* pSection,
746 __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
747 )
748{
749 HRESULT hr = S_OK;
750 LPWSTR sczSourcePath = NULL;
751 LPWSTR wzExecutableName = NULL;
752
753 hr = PathForCurrentProcess(&sczSourcePath, NULL);
754 ExitOnFailure(hr, "Failed to get current path for process to cache to clean room.");
755
756 wzExecutableName = PathFile(sczSourcePath);
757
758 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczCleanRoomBundlePath);
759 ExitOnFailure(hr, "Failed to cache bundle to clean room.");
760
761LExit:
762 ReleaseStr(sczSourcePath);
763
764 return hr;
765}
766
767extern "C" HRESULT CacheBundleToWorkingDirectory(
768 __in_z LPCWSTR /*wzBundleId*/,
769 __in_z LPCWSTR wzExecutableName,
770 __in BURN_SECTION* pSection,
771 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
772 )
773{
774 Assert(vfInitializedCache);
775
776 HRESULT hr = S_OK;
777 LPWSTR sczSourcePath = NULL;
778
779 // Initialize the source.
780 hr = PathForCurrentProcess(&sczSourcePath, NULL);
781 ExitOnFailure(hr, "Failed to get current process path.");
782
783 // If the bundle is running out of the package cache then we don't need to copy it to
784 // the working folder since we feel safe in the package cache and will run from there.
785 if (vfRunningFromCache)
786 {
787 hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0);
788 ExitOnFailure(hr, "Failed to use current process path as target path.");
789 }
790 else // otherwise, carry on putting the bundle in the working folder.
791 {
792 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pSection, psczEngineWorkingPath);
793 ExitOnFailure(hr, "Failed to copy engine to working folder.");
794 }
795
796LExit:
797 ReleaseStr(sczSourcePath);
798
799 return hr;
800}
801
802extern "C" HRESULT CacheLayoutBundle(
803 __in_z LPCWSTR wzExecutableName,
804 __in_z LPCWSTR wzLayoutDirectory,
805 __in_z LPCWSTR wzSourceBundlePath,
806 __in DWORD64 qwBundleSize,
807 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
808 __in LPPROGRESS_ROUTINE pfnProgress,
809 __in LPVOID pContext
810 )
811{
812 HRESULT hr = S_OK;
813 LPWSTR sczTargetPath = NULL;
814
815 hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath);
816 ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout.");
817
818 LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
819
820 hr = CacheTransferFileWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, BURN_CACHE_STEP_FINALIZE, qwBundleSize, pfnCacheMessageHandler, pfnProgress, pContext);
821 ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
822
823LExit:
824 ReleaseStr(sczTargetPath);
825
826 return hr;
827}
828
829extern "C" HRESULT CacheCompleteBundle(
830 __in BOOL fPerMachine,
831 __in_z LPCWSTR wzExecutableName,
832 __in_z LPCWSTR wzBundleId,
833 __in_z LPCWSTR wzSourceBundlePath
834#ifdef DEBUG
835 , __in_z LPCWSTR wzExecutablePath
836#endif
837 )
838{
839 HRESULT hr = S_OK;
840 int nCompare = 0;
841 LPWSTR sczTargetDirectory = NULL;
842 LPWSTR sczTargetPath = NULL;
843 LPWSTR sczSourceDirectory = NULL;
844 LPWSTR sczPayloadSourcePath = NULL;
845
846 hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory);
847 ExitOnFailure(hr, "Failed to create completed cache path for bundle.");
848
849 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
850 ExitOnFailure(hr, "Failed to combine completed path with engine file name.");
851
852 // We can't just use wzExecutablePath because we needed to call CreateCompletedPath to ensure that the destination was secured.
853 Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1));
854
855 // If the bundle is running out of the package cache then we don't need to copy it there
856 // (and don't want to since it'll be in use) so bail.
857 hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare);
858 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
859
860 if (CSTR_EQUAL == nCompare)
861 {
862 ExitFunction();
863 }
864
865 // Otherwise, carry on putting the bundle in the cache.
866 LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
867
868 FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart.
869
870 hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
871 ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
872
873 // Reset the path permissions in the cache.
874 hr = ResetPathPermissions(fPerMachine, sczTargetPath);
875 ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath);
876
877 hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory);
878 ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath);
879
880LExit:
881 ReleaseStr(sczPayloadSourcePath);
882 ReleaseStr(sczSourceDirectory);
883 ReleaseStr(sczTargetPath);
884 ReleaseStr(sczTargetDirectory);
885
886 return hr;
887}
888
889extern "C" HRESULT CacheLayoutContainer(
890 __in BURN_CONTAINER* pContainer,
891 __in_z_opt LPCWSTR wzLayoutDirectory,
892 __in_z LPCWSTR wzUnverifiedContainerPath,
893 __in BOOL fMove,
894 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
895 __in LPPROGRESS_ROUTINE pfnProgress,
896 __in LPVOID pContext
897 )
898{
899 HRESULT hr = S_OK;
900 LPWSTR sczCachedPath = NULL;
901
902 hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath);
903 ExitOnFailure(hr, "Failed to concat complete cached path.");
904
905 hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
906 ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath);
907
908LExit:
909 ReleaseStr(sczCachedPath);
910
911 return hr;
912}
913
914extern "C" HRESULT CacheLayoutPayload(
915 __in BURN_PAYLOAD* pPayload,
916 __in_z_opt LPCWSTR wzLayoutDirectory,
917 __in_z LPCWSTR wzUnverifiedPayloadPath,
918 __in BOOL fMove,
919 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
920 __in LPPROGRESS_ROUTINE pfnProgress,
921 __in LPVOID pContext
922 )
923{
924 HRESULT hr = S_OK;
925 LPWSTR sczCachedPath = NULL;
926
927 hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath);
928 ExitOnFailure(hr, "Failed to concat complete cached path.");
929
930 hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove, pfnCacheMessageHandler, pfnProgress, pContext);
931 ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath);
932
933LExit:
934 ReleaseStr(sczCachedPath);
935
936 return hr;
937}
938
939extern "C" HRESULT CacheCompletePayload(
940 __in BOOL fPerMachine,
941 __in BURN_PAYLOAD* pPayload,
942 __in_z LPCWSTR wzCacheId,
943 __in_z LPCWSTR wzWorkingPayloadPath,
944 __in BOOL fMove,
945 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
946 __in LPPROGRESS_ROUTINE pfnProgress,
947 __in LPVOID pContext
948 )
949{
950 HRESULT hr = S_OK;
951 LPWSTR sczCachedDirectory = NULL;
952 LPWSTR sczCachedPath = NULL;
953 LPWSTR sczUnverifiedPayloadPath = NULL;
954
955 hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory);
956 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId);
957
958 hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
959 ExitOnFailure(hr, "Failed to concat complete cached path.");
960
961 // If the cached file matches what we expected, we're good.
962 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_VERIFY, pfnCacheMessageHandler, pfnProgress, pContext);
963 if (SUCCEEDED(hr))
964 {
965 ExitFunction();
966 }
967
968 hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath);
969 ExitOnFailure(hr, "Failed to create unverified path.");
970
971 // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file.
972 if (FileExistsEx(wzWorkingPayloadPath, NULL))
973 {
974 hr = CacheTransferFileWithRetry(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove, BURN_CACHE_STEP_STAGE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
975 ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey);
976 }
977 else if (FileExistsEx(sczUnverifiedPayloadPath, NULL))
978 {
979 // Make sure the staging progress is sent even though there was nothing to do.
980 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, BURN_CACHE_STEP_STAGE);
981 if (SUCCEEDED(hr))
982 {
983 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, pPayload->qwFileSize);
984 }
985 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
986 ExitOnFailure(hr, "Aborted transferring working path to unverified path for payload: %ls.", pPayload->sczKey);
987 }
988 else // if the working path and unverified path do not exist, nothing we can do.
989 {
990 hr = E_FILENOTFOUND;
991 ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath);
992 }
993
994 hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath);
995 ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey);
996
997 hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath, FALSE, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
998 LogExitOnFailure(hr, MSG_FAILED_VERIFY_PAYLOAD, "Failed to verify payload: %ls at path: %ls", pPayload->sczKey, sczUnverifiedPayloadPath, NULL);
999
1000 LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath);
1001
1002 hr = CacheTransferFileWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1003 ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath);
1004
1005 ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
1006
1007LExit:
1008 ReleaseStr(sczUnverifiedPayloadPath);
1009 ReleaseStr(sczCachedPath);
1010 ReleaseStr(sczCachedDirectory);
1011
1012 return hr;
1013}
1014
1015extern "C" HRESULT CacheVerifyContainer(
1016 __in BURN_CONTAINER* pContainer,
1017 __in_z LPCWSTR wzCachedDirectory,
1018 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1019 __in LPPROGRESS_ROUTINE pfnProgress,
1020 __in LPVOID pContext
1021 )
1022{
1023 HRESULT hr = S_OK;
1024 LPWSTR sczCachedPath = NULL;
1025
1026 hr = PathConcat(wzCachedDirectory, pContainer->sczFilePath, &sczCachedPath);
1027 ExitOnFailure(hr, "Failed to concat complete cached path.");
1028
1029 hr = VerifyFileAgainstContainer(pContainer, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1030
1031LExit:
1032 ReleaseStr(sczCachedPath);
1033
1034 return hr;
1035}
1036
1037extern "C" HRESULT CacheVerifyPayload(
1038 __in BURN_PAYLOAD* pPayload,
1039 __in_z LPCWSTR wzCachedDirectory,
1040 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1041 __in LPPROGRESS_ROUTINE pfnProgress,
1042 __in LPVOID pContext
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 LPWSTR sczCachedPath = NULL;
1047
1048 hr = PathConcat(wzCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
1049 ExitOnFailure(hr, "Failed to concat complete cached path.");
1050
1051 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath, TRUE, BURN_CACHE_STEP_HASH_TO_SKIP_ACQUIRE, pfnCacheMessageHandler, pfnProgress, pContext);
1052
1053LExit:
1054 ReleaseStr(sczCachedPath);
1055
1056 return hr;
1057}
1058
1059extern "C" HRESULT CacheRemoveWorkingFolder(
1060 __in_z_opt LPCWSTR wzBundleId
1061 )
1062{
1063 HRESULT hr = S_OK;
1064 LPWSTR sczWorkingFolder = NULL;
1065
1066 if (vfInitializedCache)
1067 {
1068 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
1069 ExitOnFailure(hr, "Failed to calculate the working folder to remove it.");
1070
1071 // Try to clean out everything in the working folder.
1072 hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1073 TraceError(hr, "Could not delete bundle engine working folder.");
1074 }
1075
1076LExit:
1077 ReleaseStr(sczWorkingFolder);
1078
1079 return hr;
1080}
1081
1082extern "C" HRESULT CacheRemoveBundle(
1083 __in BOOL fPerMachine,
1084 __in_z LPCWSTR wzBundleId
1085 )
1086{
1087 HRESULT hr = S_OK;
1088
1089 hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId);
1090 ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId);
1091
1092LExit:
1093 return hr;
1094}
1095
1096extern "C" HRESULT CacheRemovePackage(
1097 __in BOOL fPerMachine,
1098 __in_z LPCWSTR wzPackageId,
1099 __in_z LPCWSTR wzCacheId
1100 )
1101{
1102 HRESULT hr = S_OK;
1103
1104 hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId);
1105 ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId);
1106
1107LExit:
1108 return hr;
1109}
1110
1111extern "C" void CacheCleanup(
1112 __in BOOL fPerMachine,
1113 __in_z LPCWSTR wzBundleId
1114 )
1115{
1116 HRESULT hr = S_OK;
1117 LPWSTR sczFolder = NULL;
1118 LPWSTR sczFiles = NULL;
1119 LPWSTR sczDelete = NULL;
1120 HANDLE hFind = INVALID_HANDLE_VALUE;
1121 WIN32_FIND_DATAW wfd = { };
1122 size_t cchFileName = 0;
1123
1124 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder);
1125 if (SUCCEEDED(hr))
1126 {
1127 hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1128 }
1129
1130 if (!fPerMachine)
1131 {
1132 hr = CalculateWorkingFolder(wzBundleId, &sczFolder);
1133 if (SUCCEEDED(hr))
1134 {
1135 hr = PathConcat(sczFolder, L"*.*", &sczFiles);
1136 if (SUCCEEDED(hr))
1137 {
1138 hFind = ::FindFirstFileW(sczFiles, &wfd);
1139 if (INVALID_HANDLE_VALUE != hFind)
1140 {
1141 do
1142 {
1143 // Skip directories.
1144 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1145 {
1146 continue;
1147 }
1148
1149 // Skip resume files (they end with ".R").
1150 hr = ::StringCchLengthW(wfd.cFileName, MAX_PATH, &cchFileName);
1151 if (FAILED(hr) ||
1152 2 < cchFileName && L'.' == wfd.cFileName[cchFileName - 2] && (L'R' == wfd.cFileName[cchFileName - 1] || L'r' == wfd.cFileName[cchFileName - 1]))
1153 {
1154 continue;
1155 }
1156
1157 hr = PathConcatCch(sczFolder, 0, wfd.cFileName, cchFileName, &sczDelete);
1158 if (SUCCEEDED(hr))
1159 {
1160 hr = FileEnsureDelete(sczDelete);
1161 }
1162 } while (::FindNextFileW(hFind, &wfd));
1163 }
1164 }
1165 }
1166 }
1167
1168 if (INVALID_HANDLE_VALUE != hFind)
1169 {
1170 ::FindClose(hFind);
1171 }
1172
1173 ReleaseStr(sczDelete);
1174 ReleaseStr(sczFiles);
1175 ReleaseStr(sczFolder);
1176}
1177
1178extern "C" void CacheUninitialize()
1179{
1180 ReleaseNullStr(vsczCurrentMachinePackageCache);
1181 ReleaseNullStr(vsczDefaultMachinePackageCache);
1182 ReleaseNullStr(vsczDefaultUserPackageCache);
1183 ReleaseNullStr(vsczWorkingFolder);
1184 ReleaseNullStr(vsczSourceProcessFolder);
1185
1186 vfRunningFromCache = FALSE;
1187 vfInitializedCache = FALSE;
1188}
1189
1190// Internal functions.
1191
1192static HRESULT CalculateWorkingFolder(
1193 __in_z_opt LPCWSTR /*wzBundleId*/,
1194 __deref_out_z LPWSTR* psczWorkingFolder
1195 )
1196{
1197 HRESULT hr = S_OK;
1198 RPC_STATUS rs = RPC_S_OK;
1199 BOOL fElevated = FALSE;
1200 WCHAR wzTempPath[MAX_PATH] = { };
1201 UUID guid = {};
1202 WCHAR wzGuid[39];
1203
1204 if (!vsczWorkingFolder)
1205 {
1206 ProcElevated(::GetCurrentProcess(), &fElevated);
1207
1208 if (fElevated)
1209 {
1210 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
1211 {
1212 ExitWithLastError(hr, "Failed to get windows path for working folder.");
1213 }
1214
1215 hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath));
1216 ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash.");
1217
1218 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\");
1219 ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder.");
1220 }
1221 else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath))
1222 {
1223 ExitWithLastError(hr, "Failed to get temp path for working folder.");
1224 }
1225
1226 rs = ::UuidCreate(&guid);
1227 hr = HRESULT_FROM_RPC(rs);
1228 ExitOnFailure(hr, "Failed to create working folder guid.");
1229
1230 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
1231 {
1232 hr = E_OUTOFMEMORY;
1233 ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1234 }
1235
1236 hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid);
1237 ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder.");
1238 }
1239
1240 hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0);
1241 ExitOnFailure(hr, "Failed to copy working folder path.");
1242
1243LExit:
1244 return hr;
1245}
1246
1247static HRESULT GetRootPath(
1248 __in BOOL fPerMachine,
1249 __in BOOL fAllowRedirect,
1250 __deref_out_z LPWSTR* psczRootPath
1251 )
1252{
1253 HRESULT hr = S_OK;
1254 LPWSTR sczAppData = NULL;
1255 int nCompare = 0;
1256
1257 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
1258 if (fPerMachine)
1259 {
1260 // Always construct the default machine package cache path so we can determine if we're redirected.
1261 if (!vsczDefaultMachinePackageCache)
1262 {
1263 hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData);
1264 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
1265
1266 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache);
1267 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine");
1268
1269 hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache);
1270 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine");
1271 }
1272
1273 if (!vsczCurrentMachinePackageCache)
1274 {
1275 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache);
1276 ExitOnFailure(hr, "Failed to read PackageCache policy directory.");
1277
1278 if (vsczCurrentMachinePackageCache)
1279 {
1280 hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache);
1281 ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name.");
1282 }
1283 else
1284 {
1285 hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0);
1286 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
1287 }
1288 }
1289
1290 hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0);
1291 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine");
1292
1293 hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare);
1294 ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
1295
1296 // Return S_FALSE if the current location is not the default location (redirected).
1297 hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE;
1298 }
1299 else
1300 {
1301 if (!vsczDefaultUserPackageCache)
1302 {
1303 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData);
1304 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
1305
1306 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache);
1307 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user");
1308
1309 hr = PathBackslashTerminate(&vsczDefaultUserPackageCache);
1310 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user");
1311 }
1312
1313 hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0);
1314 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user");
1315 }
1316
1317LExit:
1318 ReleaseStr(sczAppData);
1319
1320 return hr;
1321}
1322
1323static HRESULT GetLastUsedSourceFolder(
1324 __in BURN_VARIABLES* pVariables,
1325 __out_z LPWSTR* psczLastSource
1326 )
1327{
1328 HRESULT hr = S_OK;
1329
1330 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1331 if (E_NOTFOUND == hr)
1332 {
1333 // Try the original source folder.
1334 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, psczLastSource);
1335 }
1336
1337 return hr;
1338}
1339
1340static HRESULT CreateCompletedPath(
1341 __in BOOL fPerMachine,
1342 __in LPCWSTR wzId,
1343 __out LPWSTR* psczCacheDirectory
1344 )
1345{
1346 static BOOL fPerMachineCacheRootVerified = FALSE;
1347
1348 HRESULT hr = S_OK;
1349 LPWSTR sczCacheDirectory = NULL;
1350
1351 // If we are doing a permachine install but have not yet verified that the root cache folder
1352 // was created with the correct ACLs yet, do that now.
1353 if (fPerMachine && !fPerMachineCacheRootVerified)
1354 {
1355 hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory);
1356 ExitOnFailure(hr, "Failed to get cache directory.");
1357
1358 hr = DirEnsureExists(sczCacheDirectory, NULL);
1359 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1360
1361 hr = SecurePath(sczCacheDirectory);
1362 ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory);
1363
1364 fPerMachineCacheRootVerified = TRUE;
1365 }
1366
1367 // Get the cache completed path, ensure it exists, and reset any permissions people
1368 // might have tried to set on the directory so we inherit the (correct!) security
1369 // permissions from the parent directory.
1370 hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory);
1371 ExitOnFailure(hr, "Failed to get cache directory.");
1372
1373 hr = DirEnsureExists(sczCacheDirectory, NULL);
1374 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1375
1376 ResetPathPermissions(fPerMachine, sczCacheDirectory);
1377
1378 *psczCacheDirectory = sczCacheDirectory;
1379 sczCacheDirectory = NULL;
1380
1381LExit:
1382 ReleaseStr(sczCacheDirectory);
1383 return hr;
1384}
1385
1386static HRESULT CreateUnverifiedPath(
1387 __in BOOL fPerMachine,
1388 __in_z LPCWSTR wzPayloadId,
1389 __out_z LPWSTR* psczUnverifiedPayloadPath
1390 )
1391{
1392 static BOOL fUnverifiedCacheFolderCreated = FALSE;
1393
1394 HRESULT hr = S_OK;
1395 LPWSTR sczUnverifiedCacheFolder = NULL;
1396
1397 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder);
1398 ExitOnFailure(hr, "Failed to get cache directory.");
1399
1400 if (!fUnverifiedCacheFolderCreated)
1401 {
1402 hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL);
1403 ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder);
1404
1405 ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder);
1406 }
1407
1408 hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath);
1409 ExitOnFailure(hr, "Failed to concat payload id to unverified folder path.");
1410
1411LExit:
1412 ReleaseStr(sczUnverifiedCacheFolder);
1413
1414 return hr;
1415}
1416
1417static HRESULT VerifyThenTransferContainer(
1418 __in BURN_CONTAINER* pContainer,
1419 __in_z LPCWSTR wzCachedPath,
1420 __in_z LPCWSTR wzUnverifiedContainerPath,
1421 __in BOOL fMove,
1422 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1423 __in LPPROGRESS_ROUTINE pfnProgress,
1424 __in LPVOID pContext
1425 )
1426{
1427 HRESULT hr = S_OK;
1428 HANDLE hFile = INVALID_HANDLE_VALUE;
1429
1430 // Get the container on disk actual hash.
1431 hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1432 if (INVALID_HANDLE_VALUE == hFile)
1433 {
1434 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1435 }
1436
1437 // Container should have a hash we can use to verify with.
1438 if (pContainer->pbHash)
1439 {
1440 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzUnverifiedContainerPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1441 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1442 }
1443
1444 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
1445
1446 hr = CacheTransferFileWithRetry(wzUnverifiedContainerPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pContainer->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1447
1448LExit:
1449 ReleaseFileHandle(hFile);
1450
1451 return hr;
1452}
1453
1454static HRESULT VerifyThenTransferPayload(
1455 __in BURN_PAYLOAD* pPayload,
1456 __in_z LPCWSTR wzCachedPath,
1457 __in_z LPCWSTR wzUnverifiedPayloadPath,
1458 __in BOOL fMove,
1459 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1460 __in LPPROGRESS_ROUTINE pfnProgress,
1461 __in LPVOID pContext
1462 )
1463{
1464 HRESULT hr = S_OK;
1465 HANDLE hFile = INVALID_HANDLE_VALUE;
1466
1467 // Get the payload on disk actual hash.
1468 hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1469 if (INVALID_HANDLE_VALUE == hFile)
1470 {
1471 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1472 }
1473
1474 if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1475 {
1476 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1477 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1478 }
1479
1480 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
1481
1482 hr = CacheTransferFileWithRetry(wzUnverifiedPayloadPath, wzCachedPath, fMove, BURN_CACHE_STEP_FINALIZE, pPayload->qwFileSize, pfnCacheMessageHandler, pfnProgress, pContext);
1483
1484LExit:
1485 ReleaseFileHandle(hFile);
1486
1487 return hr;
1488}
1489
1490static HRESULT CacheTransferFileWithRetry(
1491 __in_z LPCWSTR wzSourcePath,
1492 __in_z LPCWSTR wzDestinationPath,
1493 __in BOOL fMove,
1494 __in BURN_CACHE_STEP cacheStep,
1495 __in DWORD64 qwFileSize,
1496 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1497 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1498 __in LPVOID pContext
1499 )
1500{
1501 HRESULT hr = S_OK;
1502
1503 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1504 ExitOnFailure(hr, "Aborted cache file transfer begin.");
1505
1506 // TODO: send progress during the file transfer.
1507 if (fMove)
1508 {
1509 hr = FileEnsureMoveWithRetry(wzSourcePath, wzDestinationPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1510 ExitOnFailure(hr, "Failed to move %ls to %ls", wzSourcePath, wzDestinationPath);
1511 }
1512 else
1513 {
1514 hr = FileEnsureCopyWithRetry(wzSourcePath, wzDestinationPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1515 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzSourcePath, wzDestinationPath);
1516 }
1517
1518 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
1519
1520LExit:
1521 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1522
1523 return hr;
1524}
1525
1526static HRESULT VerifyFileAgainstContainer(
1527 __in BURN_CONTAINER* pContainer,
1528 __in_z LPCWSTR wzVerifyPath,
1529 __in BOOL fAlreadyCached,
1530 __in BURN_CACHE_STEP cacheStep,
1531 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1532 __in LPPROGRESS_ROUTINE pfnProgress,
1533 __in LPVOID pContext
1534 )
1535{
1536 HRESULT hr = S_OK;
1537 HANDLE hFile = INVALID_HANDLE_VALUE;
1538
1539 // Get the container on disk actual hash.
1540 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1541 if (INVALID_HANDLE_VALUE == hFile)
1542 {
1543 hr = HRESULT_FROM_WIN32(::GetLastError());
1544 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1545 {
1546 ExitFunction(); // do not log error when the file was not found.
1547 }
1548 ExitOnRootFailure(hr, "Failed to open container at path: %ls", wzVerifyPath);
1549 }
1550
1551 if (pContainer->pbHash) // the container should have a hash we can use to verify it.
1552 {
1553 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, pContainer->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1554 ExitOnFailure(hr, "Failed to verify hash of container: %ls", pContainer->sczId);
1555 }
1556
1557 if (fAlreadyCached)
1558 {
1559 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_CONTAINER, pContainer->sczId, wzVerifyPath);
1560 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1561 }
1562
1563LExit:
1564 ReleaseFileHandle(hFile);
1565
1566 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1567 {
1568 if (fAlreadyCached)
1569 {
1570 LogErrorId(hr, MSG_FAILED_VERIFY_CONTAINER, pContainer->sczId, wzVerifyPath, NULL);
1571 }
1572
1573 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1574 }
1575
1576 return hr;
1577}
1578
1579static HRESULT VerifyFileAgainstPayload(
1580 __in BURN_PAYLOAD* pPayload,
1581 __in_z LPCWSTR wzVerifyPath,
1582 __in BOOL fAlreadyCached,
1583 __in BURN_CACHE_STEP cacheStep,
1584 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1585 __in LPPROGRESS_ROUTINE pfnProgress,
1586 __in LPVOID pContext
1587 )
1588{
1589 HRESULT hr = S_OK;
1590 HANDLE hFile = INVALID_HANDLE_VALUE;
1591
1592 // Get the payload on disk actual hash.
1593 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1594 if (INVALID_HANDLE_VALUE == hFile)
1595 {
1596 hr = HRESULT_FROM_WIN32(::GetLastError());
1597 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1598 {
1599 ExitFunction(); // do not log error when the file was not found.
1600 }
1601 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1602 }
1603
1604 if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1605 {
1606 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, wzVerifyPath, hFile, cacheStep, pfnCacheMessageHandler, pfnProgress, pContext);
1607 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1608 }
1609
1610 if (fAlreadyCached)
1611 {
1612 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, wzVerifyPath);
1613 ::DecryptFileW(wzVerifyPath, 0); // Let's try to make sure it's not encrypted.
1614 }
1615
1616LExit:
1617 ReleaseFileHandle(hFile);
1618
1619 if (FAILED(hr) && E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
1620 {
1621 if (fAlreadyCached)
1622 {
1623 LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, wzVerifyPath, NULL);
1624 }
1625
1626 FileEnsureDelete(wzVerifyPath); // if the file existed but did not verify correctly, make it go away.
1627 }
1628
1629 return hr;
1630}
1631
1632static HRESULT AllocateSid(
1633 __in WELL_KNOWN_SID_TYPE type,
1634 __out PSID* ppSid
1635 )
1636{
1637 HRESULT hr = S_OK;
1638 PSID pAllocSid = NULL;
1639 DWORD cbSid = SECURITY_MAX_SID_SIZE;
1640
1641 pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
1642 ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID.");
1643
1644 if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid))
1645 {
1646 ExitWithLastError(hr, "Failed to create well known SID.");
1647 }
1648
1649 *ppSid = pAllocSid;
1650 pAllocSid = NULL;
1651
1652LExit:
1653 ReleaseMem(pAllocSid);
1654 return hr;
1655}
1656
1657
1658static HRESULT ResetPathPermissions(
1659 __in BOOL fPerMachine,
1660 __in_z LPCWSTR wzPath
1661 )
1662{
1663 HRESULT hr = S_OK;
1664 DWORD er = ERROR_SUCCESS;
1665 DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
1666 ACL acl = { };
1667 PSID pSid = NULL;
1668
1669 if (fPerMachine)
1670 {
1671 hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid);
1672 ExitOnFailure(hr, "Failed to allocate administrator SID.");
1673
1674 // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent.
1675 if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION))
1676 {
1677 ExitWithLastError(hr, "Failed to initialize ACL.");
1678 }
1679
1680 dwSetSecurity |= OWNER_SECURITY_INFORMATION;
1681 }
1682
1683 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1684 ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath);
1685
1686 ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits.
1687
1688LExit:
1689 ReleaseMem(pSid);
1690 return hr;
1691}
1692
1693
1694static HRESULT GrantAccessAndAllocateSid(
1695 __in WELL_KNOWN_SID_TYPE type,
1696 __in DWORD dwGrantAccess,
1697 __in EXPLICIT_ACCESS* pAccess
1698 )
1699{
1700 HRESULT hr = S_OK;
1701
1702 hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName));
1703 ExitOnFailure(hr, "Failed to allocate SID to grate access.");
1704
1705 pAccess->grfAccessMode = GRANT_ACCESS;
1706 pAccess->grfAccessPermissions = dwGrantAccess;
1707 pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
1708 pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID;
1709 pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
1710
1711LExit:
1712 return hr;
1713}
1714
1715
1716static HRESULT SecurePath(
1717 __in LPCWSTR wzPath
1718 )
1719{
1720 HRESULT hr = S_OK;
1721 DWORD er = ERROR_SUCCESS;
1722 EXPLICIT_ACCESSW access[4] = { };
1723 PACL pAcl = NULL;
1724
1725 // Administrators must be the first one in the array so we can reuse the allocated SID below.
1726 hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]);
1727 ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath);
1728
1729 hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]);
1730 ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath);
1731
1732 hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]);
1733 ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath);
1734
1735 hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]);
1736 ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath);
1737
1738 er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl);
1739 ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath);
1740
1741 // Set the ACL and ensure the Administrators group ends up the owner
1742 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
1743 reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1744 ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath);
1745
1746LExit:
1747 if (pAcl)
1748 {
1749 ::LocalFree(pAcl);
1750 }
1751
1752 for (DWORD i = 0; i < countof(access); ++i)
1753 {
1754 ReleaseMem(access[i].Trustee.ptstrName);
1755 }
1756
1757 return hr;
1758}
1759
1760
1761static HRESULT CopyEngineToWorkingFolder(
1762 __in_z LPCWSTR wzSourcePath,
1763 __in_z LPCWSTR wzWorkingFolderName,
1764 __in_z LPCWSTR wzExecutableName,
1765 __in BURN_SECTION* pSection,
1766 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
1767 )
1768{
1769 HRESULT hr = S_OK;
1770 LPWSTR sczWorkingFolder = NULL;
1771 LPWSTR sczTargetDirectory = NULL;
1772 LPWSTR sczTargetPath = NULL;
1773 LPWSTR sczSourceDirectory = NULL;
1774 LPWSTR sczPayloadSourcePath = NULL;
1775 LPWSTR sczPayloadTargetPath = NULL;
1776
1777 hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder);
1778 ExitOnFailure(hr, "Failed to create working path to copy engine.");
1779
1780 hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory);
1781 ExitOnFailure(hr, "Failed to calculate the bundle working folder target name.");
1782
1783 hr = DirEnsureExists(sczTargetDirectory, NULL);
1784 ExitOnFailure(hr, "Failed create bundle working folder.");
1785
1786 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
1787 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
1788
1789 // Copy the engine without any attached containers to the working path.
1790 hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection);
1791 ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath);
1792
1793 if (psczEngineWorkingPath)
1794 {
1795 hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0);
1796 ExitOnFailure(hr, "Failed to copy target path for engine working path.");
1797 }
1798
1799LExit:
1800 ReleaseStr(sczPayloadTargetPath);
1801 ReleaseStr(sczPayloadSourcePath);
1802 ReleaseStr(sczSourceDirectory);
1803 ReleaseStr(sczTargetPath);
1804 ReleaseStr(sczTargetDirectory);
1805 ReleaseStr(sczWorkingFolder);
1806
1807 return hr;
1808}
1809
1810
1811static HRESULT CopyEngineWithSignatureFixup(
1812 __in HANDLE hEngineFile,
1813 __in_z LPCWSTR wzEnginePath,
1814 __in_z LPCWSTR wzTargetPath,
1815 __in BURN_SECTION* pSection
1816 )
1817{
1818 HRESULT hr = S_OK;
1819 HANDLE hTarget = INVALID_HANDLE_VALUE;
1820 LARGE_INTEGER li = { };
1821 DWORD dwZeroOriginals[3] = { };
1822
1823 hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1824 if (INVALID_HANDLE_VALUE == hTarget)
1825 {
1826 ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath);
1827 }
1828
1829 hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN);
1830 ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath);
1831
1832 hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL);
1833 ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath);
1834
1835 // If the original executable was signed, let's put back the checksum and signature.
1836 if (pSection->dwOriginalSignatureOffset)
1837 {
1838 // Fix up the checksum.
1839 li.QuadPart = pSection->dwChecksumOffset;
1840 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1841 {
1842 ExitWithLastError(hr, "Failed to seek to checksum in exe header.");
1843 }
1844
1845 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum));
1846 ExitOnFailure(hr, "Failed to update signature offset.");
1847
1848 // Fix up the signature information.
1849 li.QuadPart = pSection->dwCertificateTableOffset;
1850 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1851 {
1852 ExitWithLastError(hr, "Failed to seek to signature table in exe header.");
1853 }
1854
1855 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset));
1856 ExitOnFailure(hr, "Failed to update signature offset.");
1857
1858 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize));
1859 ExitOnFailure(hr, "Failed to update signature offset.");
1860
1861 // Zero out the original information since that is how it was when the file was originally signed.
1862 li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset;
1863 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1864 {
1865 ExitWithLastError(hr, "Failed to seek to original data in exe burn section header.");
1866 }
1867
1868 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals));
1869 ExitOnFailure(hr, "Failed to zero out original data offset.");
1870 }
1871
1872LExit:
1873 ReleaseFileHandle(hTarget);
1874
1875 return hr;
1876}
1877
1878
1879static HRESULT RemoveBundleOrPackage(
1880 __in BOOL fBundle,
1881 __in BOOL fPerMachine,
1882 __in_z LPCWSTR wzBundleOrPackageId,
1883 __in_z LPCWSTR wzCacheId
1884 )
1885{
1886 HRESULT hr = S_OK;
1887 LPWSTR sczRootCacheDirectory = NULL;
1888 LPWSTR sczDirectory = NULL;
1889
1890 hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory);
1891 ExitOnFailure(hr, "Failed to calculate cache path.");
1892
1893 LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory);
1894
1895 // Try really hard to remove the cache directory.
1896 hr = E_FAIL;
1897 for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry)
1898 {
1899 if (0 < iRetry)
1900 {
1901 ::Sleep(FILE_OPERATION_RETRY_WAIT);
1902 }
1903
1904 hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1905 if (E_PATHNOTFOUND == hr)
1906 {
1907 break;
1908 }
1909 }
1910
1911 if (E_PATHNOTFOUND != hr && FAILED(hr))
1912 {
1913 LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr);
1914 hr = S_OK;
1915 }
1916 else
1917 {
1918 // Try to remove root package cache in the off chance it is now empty.
1919 hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory);
1920 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1921 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1922
1923 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
1924 if (S_FALSE == hr)
1925 {
1926 hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory);
1927 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1928 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1929 }
1930 }
1931
1932LExit:
1933 ReleaseStr(sczDirectory);
1934 ReleaseStr(sczRootCacheDirectory);
1935
1936 return hr;
1937}
1938
1939static HRESULT VerifyHash(
1940 __in BYTE* pbHash,
1941 __in DWORD cbHash,
1942 __in DWORD64 qwFileSize,
1943 __in_z LPCWSTR wzUnverifiedPayloadPath,
1944 __in HANDLE hFile,
1945 __in BURN_CACHE_STEP cacheStep,
1946 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
1947 __in LPPROGRESS_ROUTINE /*pfnProgress*/,
1948 __in LPVOID pContext
1949 )
1950{
1951 UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath);
1952
1953 HRESULT hr = S_OK;
1954 BYTE rgbActualHash[SHA512_HASH_LEN] = { };
1955 DWORD64 qwHashedBytes = 0;
1956 LONGLONG llSize = 0;
1957 LPWSTR pszExpected = NULL;
1958 LPWSTR pszActual = NULL;
1959
1960 hr = SendCacheBeginMessage(pfnCacheMessageHandler, pContext, cacheStep);
1961 ExitOnFailure(hr, "Aborted cache verify hash begin.");
1962
1963 hr = FileSizeByHandle(hFile, &llSize);
1964 ExitOnFailure(hr, "Failed to get file size for path: %ls", wzUnverifiedPayloadPath);
1965
1966 if (static_cast<DWORD64>(llSize) != qwFileSize)
1967 {
1968 ExitOnFailure(hr = ERROR_FILE_CORRUPT, "File size mismatch for path: %ls, expected: %llu, actual: %lld", wzUnverifiedPayloadPath, qwFileSize, llSize);
1969 }
1970
1971 // TODO: create a cryp hash file that sends progress.
1972 hr = CrypHashFileHandle(hFile, PROV_RSA_AES, CALG_SHA_512, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes);
1973 ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath);
1974
1975 // Compare hashes.
1976 if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, sizeof(rgbActualHash)))
1977 {
1978 hr = CRYPT_E_HASH_VALUE;
1979
1980 // Best effort to log the expected and actual hash value strings.
1981 if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) &&
1982 SUCCEEDED(StrAllocHexEncode(rgbActualHash, sizeof(rgbActualHash), &pszActual)))
1983 {
1984 ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual);
1985 }
1986 else
1987 {
1988 ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath);
1989 }
1990 }
1991
1992 hr = SendCacheSuccessMessage(pfnCacheMessageHandler, pContext, qwFileSize);
1993
1994LExit:
1995 SendCacheCompleteMessage(pfnCacheMessageHandler, pContext, hr);
1996
1997 ReleaseStr(pszActual);
1998 ReleaseStr(pszExpected);
1999
2000 return hr;
2001}
2002
2003static HRESULT SendCacheBeginMessage(
2004 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2005 __in LPVOID pContext,
2006 __in BURN_CACHE_STEP cacheStep
2007 )
2008{
2009 HRESULT hr = S_OK;
2010 BURN_CACHE_MESSAGE message = { };
2011
2012 message.type = BURN_CACHE_MESSAGE_BEGIN;
2013 message.begin.cacheStep = cacheStep;
2014
2015 hr = pfnCacheMessageHandler(&message, pContext);
2016
2017 return hr;
2018}
2019
2020static HRESULT SendCacheSuccessMessage(
2021 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2022 __in LPVOID pContext,
2023 __in DWORD64 qwFileSize
2024 )
2025{
2026 HRESULT hr = S_OK;
2027 BURN_CACHE_MESSAGE message = { };
2028
2029 message.type = BURN_CACHE_MESSAGE_SUCCESS;
2030 message.success.qwFileSize = qwFileSize;
2031
2032 hr = pfnCacheMessageHandler(&message, pContext);
2033
2034 return hr;
2035}
2036
2037static HRESULT SendCacheCompleteMessage(
2038 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2039 __in LPVOID pContext,
2040 __in HRESULT hrStatus
2041 )
2042{
2043 HRESULT hr = S_OK;
2044 BURN_CACHE_MESSAGE message = { };
2045
2046 message.type = BURN_CACHE_MESSAGE_COMPLETE;
2047 message.complete.hrStatus = hrStatus;
2048
2049 hr = pfnCacheMessageHandler(&message, pContext);
2050
2051 return hr;
2052}