aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-06-03 17:47:54 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-06-07 19:44:36 -0500
commitb652e93a460b4b822a01382e5992f96f1d805ffe (patch)
tree0c8ec0f0eba23d65fd404eb3f510944b244de65b
parent8a4d03207633e9fdc364aaed82bd167f844679f9 (diff)
downloadwix-b652e93a460b4b822a01382e5992f96f1d805ffe.tar.gz
wix-b652e93a460b4b822a01382e5992f96f1d805ffe.tar.bz2
wix-b652e93a460b4b822a01382e5992f96f1d805ffe.zip
Replace PathCompare with PathCompareCanonicalized.
-rw-r--r--src/burn/engine/apply.cpp12
-rw-r--r--src/burn/engine/cache.cpp18
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj1
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters3
-rw-r--r--src/libs/dutil/WixToolset.DUtil/file2utl.cpp164
-rw-r--r--src/libs/dutil/WixToolset.DUtil/fileutil.cpp156
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h9
-rw-r--r--src/libs/dutil/WixToolset.DUtil/path2utl.cpp46
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp26
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp69
10 files changed, 301 insertions, 203 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index f9b33333..4ebed191 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -1350,7 +1350,7 @@ static HRESULT LayoutBundle(
1350 LPWSTR sczBundlePath = NULL; 1350 LPWSTR sczBundlePath = NULL;
1351 LPWSTR sczBundleDownloadUrl = NULL; 1351 LPWSTR sczBundleDownloadUrl = NULL;
1352 LPWSTR sczDestinationPath = NULL; 1352 LPWSTR sczDestinationPath = NULL;
1353 int nEquivalentPaths = 0; 1353 BOOL fPathEqual = FALSE;
1354 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE; 1354 BOOTSTRAPPER_CACHE_OPERATION cacheOperation = BOOTSTRAPPER_CACHE_OPERATION_NONE;
1355 BURN_CACHE_PROGRESS_CONTEXT progress = { }; 1355 BURN_CACHE_PROGRESS_CONTEXT progress = { };
1356 BOOL fRetry = FALSE; 1356 BOOL fRetry = FALSE;
@@ -1375,10 +1375,10 @@ static HRESULT LayoutBundle(
1375 ExitOnFailure(hr, "Failed to concat layout path for bundle."); 1375 ExitOnFailure(hr, "Failed to concat layout path for bundle.");
1376 1376
1377 // If the destination path is the currently running bundle, bail. 1377 // If the destination path is the currently running bundle, bail.
1378 hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths); 1378 hr = PathCompareCanonicalized(sczBundlePath, sczDestinationPath, &fPathEqual);
1379 ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path."); 1379 ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path.");
1380 1380
1381 if (CSTR_EQUAL == nEquivalentPaths && FileExistsEx(sczDestinationPath, NULL)) 1381 if (fPathEqual && FileExistsEx(sczDestinationPath, NULL))
1382 { 1382 {
1383 hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL); 1383 hr = UserExperienceOnCacheContainerOrPayloadVerifyBegin(pContext->pUX, NULL, NULL);
1384 if (FAILED(hr)) 1384 if (FAILED(hr))
@@ -1533,7 +1533,7 @@ static HRESULT AcquireContainerOrPayload(
1533 AssertSz(pContainer || pPayload, "Must provide a container or a payload."); 1533 AssertSz(pContainer || pPayload, "Must provide a container or a payload.");
1534 1534
1535 HRESULT hr = S_OK; 1535 HRESULT hr = S_OK;
1536 int nEquivalentPaths = 0; 1536 BOOL fPathEqual = FALSE;
1537 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL; 1537 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1538 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL; 1538 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1539 LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL; 1539 LPCWSTR wzPayloadContainerId = pPayload && pPayload->pContainer ? pPayload->pContainer->sczId : NULL;
@@ -1673,10 +1673,10 @@ static HRESULT AcquireContainerOrPayload(
1673 { 1673 {
1674 case BOOTSTRAPPER_CACHE_OPERATION_COPY: 1674 case BOOTSTRAPPER_CACHE_OPERATION_COPY:
1675 // If the source path and destination path are different, do the copy (otherwise there's no point). 1675 // If the source path and destination path are different, do the copy (otherwise there's no point).
1676 hr = PathCompare(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &nEquivalentPaths); 1676 hr = PathCompareCanonicalized(pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath, &fPathEqual);
1677 ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); 1677 ExitOnFailure(hr, "Failed to determine if payload paths were equivalent, source: %ls, destination: %ls.", pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1678 1678
1679 if (CSTR_EQUAL != nEquivalentPaths) 1679 if (!fPathEqual)
1680 { 1680 {
1681 hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath); 1681 hr = CopyPayload(pProgress, INVALID_HANDLE_VALUE, pContext->rgSearchPaths[dwChosenSearchPath], wzDestinationPath);
1682 ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId); 1682 ExitOnFailure(hr, "Failed to copy payload: %ls", wzPayloadId);
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index 04b2f0ca..b311a195 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -170,7 +170,7 @@ extern "C" HRESULT CacheInitialize(
170 170
171 HRESULT hr = S_OK; 171 HRESULT hr = S_OK;
172 LPWSTR sczAppData = NULL; 172 LPWSTR sczAppData = NULL;
173 int nCompare = 0; 173 BOOL fPathEqual = FALSE;
174 174
175 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. 175 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
176 // Always construct the default machine package cache path so we can determine if we're redirected. 176 // Always construct the default machine package cache path so we can determine if we're redirected.
@@ -199,10 +199,10 @@ extern "C" HRESULT CacheInitialize(
199 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory."); 199 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
200 } 200 }
201 201
202 hr = PathCompare(pCache->sczDefaultMachinePackageCache, pCache->sczCurrentMachinePackageCache, &nCompare); 202 hr = PathCompareCanonicalized(pCache->sczDefaultMachinePackageCache, pCache->sczCurrentMachinePackageCache, &fPathEqual);
203 ExitOnFailure(hr, "Failed to compare default and current package cache directories."); 203 ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
204 204
205 pCache->fCustomMachinePackageCache = CSTR_EQUAL != nCompare; 205 pCache->fCustomMachinePackageCache = !fPathEqual;
206 206
207 207
208 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); 208 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData);
@@ -241,7 +241,7 @@ extern "C" HRESULT CacheInitializeSources(
241 LPWSTR sczCompletedPath = NULL; 241 LPWSTR sczCompletedPath = NULL;
242 LPWSTR sczOriginalSource = NULL; 242 LPWSTR sczOriginalSource = NULL;
243 LPWSTR sczOriginalSourceFolder = NULL; 243 LPWSTR sczOriginalSourceFolder = NULL;
244 int nCompare = 0; 244 BOOL fPathEqual = FALSE;
245 LPCWSTR wzSourceProcessPath = pInternalCommand->sczSourceProcessPath; 245 LPCWSTR wzSourceProcessPath = pInternalCommand->sczSourceProcessPath;
246 246
247 hr = PathForCurrentProcess(&sczCurrentPath, NULL); 247 hr = PathForCurrentProcess(&sczCurrentPath, NULL);
@@ -254,10 +254,10 @@ extern "C" HRESULT CacheInitializeSources(
254 hr = PathConcatRelativeToBase(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath); 254 hr = PathConcatRelativeToBase(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
255 ExitOnFailure(hr, "Failed to combine working path with engine file name."); 255 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
256 256
257 hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare); 257 hr = PathCompareCanonicalized(sczCurrentPath, sczCompletedPath, &fPathEqual);
258 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath); 258 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
259 259
260 pCache->fRunningFromCache = (CSTR_EQUAL == nCompare); 260 pCache->fRunningFromCache = fPathEqual;
261 261
262 // If a source process path was not provided (e.g. we are not being 262 // If a source process path was not provided (e.g. we are not being
263 // run in a clean room) then use the current process path as the 263 // run in a clean room) then use the current process path as the
@@ -959,7 +959,7 @@ extern "C" HRESULT CacheCompleteBundle(
959 ) 959 )
960{ 960{
961 HRESULT hr = S_OK; 961 HRESULT hr = S_OK;
962 int nCompare = 0; 962 BOOL fPathEqual = FALSE;
963 LPWSTR sczTargetDirectory = NULL; 963 LPWSTR sczTargetDirectory = NULL;
964 LPWSTR sczTargetPath = NULL; 964 LPWSTR sczTargetPath = NULL;
965 LPWSTR sczSourceDirectory = NULL; 965 LPWSTR sczSourceDirectory = NULL;
@@ -976,10 +976,10 @@ extern "C" HRESULT CacheCompleteBundle(
976 976
977 // If the bundle is running out of the package cache then we don't need to copy it there 977 // If the bundle is running out of the package cache then we don't need to copy it there
978 // (and don't want to since it'll be in use) so bail. 978 // (and don't want to since it'll be in use) so bail.
979 hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare); 979 hr = PathCompareCanonicalized(wzSourceBundlePath, sczTargetPath, &fPathEqual);
980 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath); 980 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
981 981
982 if (CSTR_EQUAL == nCompare) 982 if (fPathEqual)
983 { 983 {
984 ExitFunction(); 984 ExitFunction();
985 } 985 }
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
index ba9e801e..8a6f3b13 100644
--- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
@@ -66,6 +66,7 @@
66 <DisableSpecificWarnings>4091;4458</DisableSpecificWarnings> 66 <DisableSpecificWarnings>4091;4458</DisableSpecificWarnings>
67 </ClCompile> 67 </ClCompile>
68 <ClCompile Include="eseutil.cpp" /> 68 <ClCompile Include="eseutil.cpp" />
69 <ClCompile Include="file2utl.cpp" />
69 <ClCompile Include="fileutil.cpp" /> 70 <ClCompile Include="fileutil.cpp" />
70 <ClCompile Include="gdiputil.cpp" /> 71 <ClCompile Include="gdiputil.cpp" />
71 <ClCompile Include="guidutil.cpp" /> 72 <ClCompile Include="guidutil.cpp" />
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
index 6444b19c..dbbe68f4 100644
--- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
@@ -69,6 +69,9 @@
69 <ClCompile Include="eseutil.cpp"> 69 <ClCompile Include="eseutil.cpp">
70 <Filter>Source Files</Filter> 70 <Filter>Source Files</Filter>
71 </ClCompile> 71 </ClCompile>
72 <ClCompile Include="file2utl.cpp">
73 <Filter>Source Files</Filter>
74 </ClCompile>
72 <ClCompile Include="fileutil.cpp"> 75 <ClCompile Include="fileutil.cpp">
73 <Filter>Source Files</Filter> 76 <Filter>Source Files</Filter>
74 </ClCompile> 77 </ClCompile>
diff --git a/src/libs/dutil/WixToolset.DUtil/file2utl.cpp b/src/libs/dutil/WixToolset.DUtil/file2utl.cpp
new file mode 100644
index 00000000..88f8377c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/file2utl.cpp
@@ -0,0 +1,164 @@
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// Exit macros
7#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
8#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
9#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
10#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
11#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
12#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
13#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
14#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
15#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
16#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
17#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__)
18#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__)
19
20// constants
21
22const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
23const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
24
25
26/*******************************************************************
27 FileExistsAfterRestart - checks that a file exists and will continue
28 to exist after restart.
29
30********************************************************************/
31extern "C" BOOL DAPI FileExistsAfterRestart(
32 __in_z LPCWSTR wzPath,
33 __out_opt DWORD *pdwAttributes
34 )
35{
36 HRESULT hr = S_OK;
37 BOOL fExists = FALSE;
38 HKEY hkPendingFileRename = NULL;
39 LPWSTR* rgsczRenames = NULL;
40 DWORD cRenames = 0;
41 BOOL fPathEqual = FALSE;
42
43 fExists = FileExistsEx(wzPath, pdwAttributes);
44 if (fExists)
45 {
46 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename);
47 if (E_FILENOTFOUND == hr)
48 {
49 ExitFunction1(hr = S_OK);
50 }
51 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
52
53 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
54 if (E_FILENOTFOUND == hr)
55 {
56 ExitFunction1(hr = S_OK);
57 }
58 FileExitOnFailure(hr, "Failed to read pending file renames.");
59
60 // The pending file renames array is pairs of source and target paths. We only care
61 // about checking the source paths so skip the target paths (i += 2).
62 for (DWORD i = 0; i < cRenames; i += 2)
63 {
64 LPWSTR wzRename = rgsczRenames[i];
65 if (wzRename && *wzRename)
66 {
67 hr = PathCompareCanonicalized(wzPath, wzRename, &fPathEqual);
68 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
69
70 if (fPathEqual)
71 {
72 fExists = FALSE;
73 break;
74 }
75 }
76 }
77 }
78
79LExit:
80 ReleaseStrArray(rgsczRenames, cRenames);
81 ReleaseRegKey(hkPendingFileRename);
82
83 return fExists;
84}
85
86
87/*******************************************************************
88 FileRemoveFromPendingRename - removes the file path from the pending
89 file rename list.
90
91********************************************************************/
92extern "C" HRESULT DAPI FileRemoveFromPendingRename(
93 __in_z LPCWSTR wzPath
94 )
95{
96 HRESULT hr = S_OK;
97 HKEY hkPendingFileRename = NULL;
98 LPWSTR* rgsczRenames = NULL;
99 DWORD cRenames = 0;
100 BOOL fPathEqual = FALSE;
101 BOOL fRemoved = FALSE;
102 DWORD cNewRenames = 0;
103
104 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename);
105 if (E_FILENOTFOUND == hr)
106 {
107 ExitFunction1(hr = S_OK);
108 }
109 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
110
111 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
112 if (E_FILENOTFOUND == hr)
113 {
114 ExitFunction1(hr = S_OK);
115 }
116 FileExitOnFailure(hr, "Failed to read pending file renames.");
117
118 // The pending file renames array is pairs of source and target paths. We only care
119 // about checking the source paths so skip the target paths (i += 2).
120 for (DWORD i = 0; i < cRenames; i += 2)
121 {
122 LPWSTR wzRename = rgsczRenames[i];
123 if (wzRename && *wzRename)
124 {
125 hr = PathCompareCanonicalized(wzPath, wzRename, &fPathEqual);
126 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
127
128 // If we find our path in the list, null out the source and target slot and
129 // we'll compact the array next.
130 if (fPathEqual)
131 {
132 ReleaseNullStr(rgsczRenames[i]);
133 ReleaseNullStr(rgsczRenames[i + 1]);
134 fRemoved = TRUE;
135 }
136 }
137 }
138
139 if (fRemoved)
140 {
141 // Compact the array by removing any nulls.
142 for (DWORD i = 0; i < cRenames; ++i)
143 {
144 LPWSTR wzRename = rgsczRenames[i];
145 if (wzRename)
146 {
147 rgsczRenames[cNewRenames] = wzRename;
148 ++cNewRenames;
149 }
150 }
151
152 cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already.
153
154 // Write the new array back to the pending file rename key.
155 hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames);
156 FileExitOnFailure(hr, "Failed to update pending file renames.");
157 }
158
159LExit:
160 ReleaseStrArray(rgsczRenames, cRenames);
161 ReleaseRegKey(hkPendingFileRename);
162
163 return hr;
164}
diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
index 2fe04de1..9f68ee52 100644
--- a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
@@ -22,9 +22,6 @@
22const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; 22const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
23const BYTE UTF16BOM[] = {0xFF, 0xFE}; 23const BYTE UTF16BOM[] = {0xFF, 0xFE};
24 24
25const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
26const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
27
28 25
29/******************************************************************* 26/*******************************************************************
30FileStripExtension - Strip extension from filename 27FileStripExtension - Strip extension from filename
@@ -527,159 +524,6 @@ extern "C" BOOL DAPI FileExistsEx(
527 524
528 525
529/******************************************************************* 526/*******************************************************************
530 FileExistsAfterRestart - checks that a file exists and will continue
531 to exist after restart.
532
533********************************************************************/
534extern "C" BOOL DAPI FileExistsAfterRestart(
535 __in_z LPCWSTR wzPath,
536 __out_opt DWORD *pdwAttributes
537 )
538{
539 HRESULT hr = S_OK;
540 BOOL fExists = FALSE;
541 HKEY hkPendingFileRename = NULL;
542 LPWSTR* rgsczRenames = NULL;
543 DWORD cRenames = 0;
544 int nCompare = 0;
545
546 fExists = FileExistsEx(wzPath, pdwAttributes);
547 if (fExists)
548 {
549 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename);
550 if (E_FILENOTFOUND == hr)
551 {
552 ExitFunction1(hr = S_OK);
553 }
554 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
555
556 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
557 if (E_FILENOTFOUND == hr)
558 {
559 ExitFunction1(hr = S_OK);
560 }
561 FileExitOnFailure(hr, "Failed to read pending file renames.");
562
563 // The pending file renames array is pairs of source and target paths. We only care
564 // about checking the source paths so skip the target paths (i += 2).
565 for (DWORD i = 0; i < cRenames; i += 2)
566 {
567 LPWSTR wzRename = rgsczRenames[i];
568 if (wzRename && *wzRename)
569 {
570 // Skip the long path designator if present.
571 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
572 {
573 wzRename += 4;
574 }
575
576 hr = PathCompare(wzPath, wzRename, &nCompare);
577 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
578
579 if (CSTR_EQUAL == nCompare)
580 {
581 fExists = FALSE;
582 break;
583 }
584 }
585 }
586 }
587
588LExit:
589 ReleaseStrArray(rgsczRenames, cRenames);
590 ReleaseRegKey(hkPendingFileRename);
591
592 return fExists;
593}
594
595
596/*******************************************************************
597 FileRemoveFromPendingRename - removes the file path from the pending
598 file rename list.
599
600********************************************************************/
601extern "C" HRESULT DAPI FileRemoveFromPendingRename(
602 __in_z LPCWSTR wzPath
603 )
604{
605 HRESULT hr = S_OK;
606 HKEY hkPendingFileRename = NULL;
607 LPWSTR* rgsczRenames = NULL;
608 DWORD cRenames = 0;
609 int nCompare = 0;
610 BOOL fRemoved = FALSE;
611 DWORD cNewRenames = 0;
612
613 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename);
614 if (E_FILENOTFOUND == hr)
615 {
616 ExitFunction1(hr = S_OK);
617 }
618 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
619
620 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
621 if (E_FILENOTFOUND == hr)
622 {
623 ExitFunction1(hr = S_OK);
624 }
625 FileExitOnFailure(hr, "Failed to read pending file renames.");
626
627 // The pending file renames array is pairs of source and target paths. We only care
628 // about checking the source paths so skip the target paths (i += 2).
629 for (DWORD i = 0; i < cRenames; i += 2)
630 {
631 LPWSTR wzRename = rgsczRenames[i];
632 if (wzRename && *wzRename)
633 {
634 // Skip the long path designator if present.
635 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
636 {
637 wzRename += 4;
638 }
639
640 hr = PathCompare(wzPath, wzRename, &nCompare);
641 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
642
643 // If we find our path in the list, null out the source and target slot and
644 // we'll compact the array next.
645 if (CSTR_EQUAL == nCompare)
646 {
647 ReleaseNullStr(rgsczRenames[i]);
648 ReleaseNullStr(rgsczRenames[i + 1]);
649 fRemoved = TRUE;
650 }
651 }
652 }
653
654 if (fRemoved)
655 {
656 // Compact the array by removing any nulls.
657 for (DWORD i = 0; i < cRenames; ++i)
658 {
659 LPWSTR wzRename = rgsczRenames[i];
660 if (wzRename)
661 {
662 rgsczRenames[cNewRenames] = wzRename;
663 ++cNewRenames;
664 }
665 }
666
667 cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already.
668
669 // Write the new array back to the pending file rename key.
670 hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames);
671 FileExitOnFailure(hr, "Failed to update pending file renames.");
672 }
673
674LExit:
675 ReleaseStrArray(rgsczRenames, cRenames);
676 ReleaseRegKey(hkPendingFileRename);
677
678 return hr;
679}
680
681
682/*******************************************************************
683 FileRead - read a file into memory 527 FileRead - read a file into memory
684 528
685********************************************************************/ 529********************************************************************/
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
index 871e706b..e64c8ef3 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -258,13 +258,14 @@ DAPI_(HRESULT) PathConcatRelativeToBase(
258 ); 258 );
259 259
260/******************************************************************* 260/*******************************************************************
261 PathCompare - compares the fully expanded path of the two paths using 261 PathCompareCanonicalized - canonicalizes the two paths using PathCanonicalizeForComparison
262 ::CompareStringW(). 262 which does not resolve relative paths into fully qualified paths.
263 The strings are then compared using ::CompareStringW().
263*******************************************************************/ 264*******************************************************************/
264DAPI_(HRESULT) PathCompare( 265DAPI_(HRESULT) PathCompareCanonicalized(
265 __in_z LPCWSTR wzPath1, 266 __in_z LPCWSTR wzPath1,
266 __in_z LPCWSTR wzPath2, 267 __in_z LPCWSTR wzPath2,
267 __out int* pnResult 268 __out BOOL* pfEqual
268 ); 269 );
269 270
270/******************************************************************* 271/*******************************************************************
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
index 61c1803a..1957a8c5 100644
--- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
@@ -118,11 +118,19 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
118 PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path"); 118 PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path");
119 } 119 }
120 120
121 if ((PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags) && 121 if (PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix &&
122 PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix) 122 (PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags))
123 { 123 {
124 hr = PathPrefix(psczCanonicalized); 124 hr = PathPrefix(psczCanonicalized);
125 PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path"); 125 PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path");
126
127 fHasPrefix = TRUE;
128 }
129
130 if (fHasPrefix)
131 {
132 // Canonicalize \??\ into \\?\.
133 (*psczCanonicalized)[1] = L'\\';
126 } 134 }
127 135
128LExit: 136LExit:
@@ -171,6 +179,40 @@ LExit:
171 return hr; 179 return hr;
172} 180}
173 181
182DAPI_(HRESULT) PathCompareCanonicalized(
183 __in_z LPCWSTR wzPath1,
184 __in_z LPCWSTR wzPath2,
185 __out BOOL* pfEqual
186 )
187{
188 HRESULT hr = S_OK;
189 LPWSTR sczCanonicalized1 = NULL;
190 LPWSTR sczCanonicalized2 = NULL;
191 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
192 int nResult = 0;
193
194 if (!wzPath1 || !wzPath2)
195 {
196 PathExitWithRootFailure(hr, E_INVALIDARG, "Both paths are required.");
197 }
198
199 hr = PathCanonicalizeForComparison(wzPath1, dwDefaultFlags, &sczCanonicalized1);
200 PathExitOnFailure(hr, "Failed to canonicalize wzPath1.");
201
202 hr = PathCanonicalizeForComparison(wzPath2, dwDefaultFlags, &sczCanonicalized2);
203 PathExitOnFailure(hr, "Failed to canonicalize wzPath2.");
204
205 nResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalized1, -1, sczCanonicalized2, -1);
206 PathExitOnNullWithLastError(nResult, hr, "Failed to compare canonicalized paths.");
207
208 *pfEqual = CSTR_EQUAL == nResult;
209
210LExit:
211 ReleaseStr(sczCanonicalized1);
212 ReleaseStr(sczCanonicalized2);
213 return hr;
214}
215
174DAPI_(HRESULT) PathDirectoryContainsPath( 216DAPI_(HRESULT) PathDirectoryContainsPath(
175 __in_z LPCWSTR wzDirectory, 217 __in_z LPCWSTR wzDirectory,
176 __in_z LPCWSTR wzPath 218 __in_z LPCWSTR wzPath
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
index a9a19b9f..abbf4d4b 100644
--- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -1080,32 +1080,6 @@ LExit:
1080} 1080}
1081 1081
1082 1082
1083DAPI_(HRESULT) PathCompare(
1084 __in_z LPCWSTR wzPath1,
1085 __in_z LPCWSTR wzPath2,
1086 __out int* pnResult
1087 )
1088{
1089 HRESULT hr = S_OK;
1090 LPWSTR sczPath1 = NULL;
1091 LPWSTR sczPath2 = NULL;
1092
1093 hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1094 PathExitOnFailure(hr, "Failed to expand path1.");
1095
1096 hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1097 PathExitOnFailure(hr, "Failed to expand path2.");
1098
1099 *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1);
1100
1101LExit:
1102 ReleaseStr(sczPath2);
1103 ReleaseStr(sczPath1);
1104
1105 return hr;
1106}
1107
1108
1109DAPI_(HRESULT) PathCompress( 1083DAPI_(HRESULT) PathCompress(
1110 __in_z LPCWSTR wzPath 1084 __in_z LPCWSTR wzPath
1111 ) 1085 )
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index 554c6f00..2505c6bf 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -151,6 +151,10 @@ namespace DutilTests
151 NativeAssert::Succeeded(hr, "Failed to canonicalize path"); 151 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
152 NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized); 152 NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized);
153 153
154 hr = PathCanonicalizeForComparison(L"\\??\\UNC\\server\\share\\dir", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
155 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
156 NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\dir", sczCanonicalized);
157
154 hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); 158 hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
155 NativeAssert::Succeeded(hr, "Failed to canonicalize path"); 159 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
156 NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", sczCanonicalized); 160 NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
@@ -203,6 +207,10 @@ namespace DutilTests
203 NativeAssert::Succeeded(hr, "Failed to canonicalize path"); 207 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
204 NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized); 208 NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized);
205 209
210 hr = PathCanonicalizeForComparison(L"\\??\\test\\..\\invalidlongpath.exe", 0, &sczCanonicalized);
211 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
212 NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized);
213
206 hr = PathCanonicalizeForComparison(L"C:\\.\\invalid:pathchars?.exe", 0, &sczCanonicalized); 214 hr = PathCanonicalizeForComparison(L"C:\\.\\invalid:pathchars?.exe", 0, &sczCanonicalized);
207 NativeAssert::Succeeded(hr, "Failed to canonicalize path"); 215 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
208 NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized); 216 NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized);
@@ -264,6 +272,67 @@ namespace DutilTests
264 } 272 }
265 273
266 [Fact] 274 [Fact]
275 void PathCompareCanonicalizeEqualTest()
276 {
277 HRESULT hr = S_OK;
278 LPWSTR sczPath = NULL;
279 BOOL fEqual = FALSE;
280 LPCWSTR rgwzPaths[14] =
281 {
282 L"C:\\simplepath", L"C:\\simplepath",
283 L"\\\\server\\share\\dir\\dir2\\..\\otherdir\\unc.exe", L"\\\\server\\share\\dir\\otherdir\\unc.exe",
284 L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", L"\\\\server\\share\\otherdir\\unc.exe",
285 L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe",
286 L"C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", L"C:\\otherdir\\pastroot.exe",
287 L"\\\\?\\C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", L"C:\\..\\otherdir\\pastroot.exe",
288 L"\\??\\C:\\dir", L"\\\\?\\C:\\dir",
289 };
290
291 try
292 {
293 for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
294 {
295 hr = PathCompareCanonicalized(rgwzPaths[i], rgwzPaths[i + 1], &fEqual);
296 NativeAssert::Succeeded(hr, "PathCompareCanonicalized: {0}, {1}", rgwzPaths[i], rgwzPaths[i + 1]);
297 Assert::True(fEqual, String::Format("PathCompareCanonicalized: {0}, {1}", gcnew String(rgwzPaths[i]), gcnew String(rgwzPaths[i + 1])));
298 }
299 }
300 finally
301 {
302 ReleaseStr(sczPath);
303 }
304 }
305
306 [Fact]
307 void PathCompareCanonicalizeNotEqualTest()
308 {
309 HRESULT hr = S_OK;
310 LPWSTR sczPath = NULL;
311 BOOL fEqual = FALSE;
312 LPCWSTR rgwzPaths[8] =
313 {
314 L"C:\\simplepath", L"D:\\simplepath",
315 L"\\\\.\\share\\otherdir\\unc.exe", L"\\\\share\\otherdir\\unc.exe",
316 L"\\\\server\\.\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe",
317 L"\\\\server\\\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe",
318 };
319
320 try
321 {
322 for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
323 {
324 hr = PathCompareCanonicalized(rgwzPaths[i], rgwzPaths[i + 1], &fEqual);
325 NativeAssert::Succeeded(hr, "PathCompareCanonicalized: {0}, {1}", rgwzPaths[i], rgwzPaths[i + 1]);
326 Assert::False(fEqual, String::Format("PathCompareCanonicalized: {0}, {1}", gcnew String(rgwzPaths[i]), gcnew String(rgwzPaths[i + 1])));
327 }
328 }
329 finally
330 {
331 ReleaseStr(sczPath);
332 }
333 }
334
335 [Fact]
267 void PathConcatRelativeToBaseTest() 336 void PathConcatRelativeToBaseTest()
268 { 337 {
269 HRESULT hr = S_OK; 338 HRESULT hr = S_OK;