summaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/RemoveFoldersEx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/RemoveFoldersEx.cpp')
-rw-r--r--src/ext/Util/ca/RemoveFoldersEx.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/ext/Util/ca/RemoveFoldersEx.cpp b/src/ext/Util/ca/RemoveFoldersEx.cpp
new file mode 100644
index 00000000..cbc7f4bb
--- /dev/null
+++ b/src/ext/Util/ca/RemoveFoldersEx.cpp
@@ -0,0 +1,243 @@
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
5LPCWSTR vcsRemoveFolderExQuery =
6 L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes`"
7 L"FROM `Wix4RemoveFolderEx``,`Component` "
8 L"WHERE `Wix4RemoveFolderEx`.`Component_`=`Component`.`Component`";
9enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition, rfqComponentAttributes };
10
11static HRESULT RecursePath(
12 __in_z LPCWSTR wzPath,
13 __in_z LPCWSTR wzId,
14 __in_z LPCWSTR wzComponent,
15 __in_z LPCWSTR wzProperty,
16 __in int iMode,
17 __in BOOL fDisableWow64Redirection,
18 __inout DWORD* pdwCounter,
19 __inout MSIHANDLE* phTable,
20 __inout MSIHANDLE* phColumns
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er;
25 LPWSTR sczSearch = NULL;
26 LPWSTR sczProperty = NULL;
27 HANDLE hFind = INVALID_HANDLE_VALUE;
28 WIN32_FIND_DATAW wfd;
29 LPWSTR sczNext = NULL;
30
31 if (fDisableWow64Redirection)
32 {
33 hr = WcaDisableWow64FSRedirection();
34 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
35 }
36
37 // First recurse down to all the child directories.
38 hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath);
39 ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath);
40
41 hFind = ::FindFirstFileW(sczSearch, &wfd);
42 if (INVALID_HANDLE_VALUE == hFind)
43 {
44 er = ::GetLastError();
45 if (ERROR_PATH_NOT_FOUND == er)
46 {
47 WcaLog(LOGMSG_STANDARD, "Search path not found: %ls; skipping", sczSearch);
48 ExitFunction1(hr = S_FALSE);
49 }
50 else
51 {
52 hr = HRESULT_FROM_WIN32(er);
53 }
54 ExitOnFailure(hr, "Failed to find all files in path: %S", wzPath);
55 }
56
57 do
58 {
59 // Skip files and the dot directories.
60 if (FILE_ATTRIBUTE_DIRECTORY != (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
61 {
62 continue;
63 }
64
65 hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName);
66 ExitOnFailure(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath);
67
68 // Don't re-disable redirection; if it was necessary, we've already done it.
69 hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, FALSE, pdwCounter, phTable, phColumns);
70 ExitOnFailure(hr, "Failed to recurse path: %S", sczNext);
71 } while (::FindNextFileW(hFind, &wfd));
72
73 er = ::GetLastError();
74 if (ERROR_NO_MORE_FILES == er)
75 {
76 hr = S_OK;
77 }
78 else
79 {
80 hr = HRESULT_FROM_WIN32(er);
81 ExitOnFailure(hr, "Failed while looping through files in directory: %S", wzPath);
82 }
83
84 // Finally, set a property that points at our path.
85 hr = StrAllocFormatted(&sczProperty, L"_%s_%u", wzProperty, *pdwCounter);
86 ExitOnFailure(hr, "Failed to allocate Property for RemoveFile table with property: %S.", wzProperty);
87
88 ++(*pdwCounter);
89
90 hr = WcaSetProperty(sczProperty, wzPath);
91 ExitOnFailure(hr, "Failed to set Property: %S with path: %S", sczProperty, wzPath);
92
93 // Add the row to remove any files and another row to remove the folder.
94 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode);
95 ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
96
97 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode);
98 ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
99
100LExit:
101 if (INVALID_HANDLE_VALUE != hFind)
102 {
103 ::FindClose(hFind);
104 }
105
106 if (fDisableWow64Redirection)
107 {
108 WcaRevertWow64FSRedirection();
109 }
110
111 ReleaseStr(sczNext);
112 ReleaseStr(sczProperty);
113 ReleaseStr(sczSearch);
114 return hr;
115}
116
117extern "C" UINT WINAPI WixRemoveFoldersEx(
118 __in MSIHANDLE hInstall
119 )
120{
121 //AssertSz(FALSE, "debug WixRemoveFoldersEx");
122
123 HRESULT hr = S_OK;
124 PMSIHANDLE hView;
125 PMSIHANDLE hRec;
126 LPWSTR sczId = NULL;
127 LPWSTR sczComponent = NULL;
128 LPWSTR sczProperty = NULL;
129 LPWSTR sczCondition = NULL;
130 LPWSTR sczPath = NULL;
131 LPWSTR sczExpandedPath = NULL;
132 int iMode = 0;
133 int iComponentAttributes;
134 BOOL f64BitComponent = FALSE;
135 DWORD dwCounter = 0;
136 DWORD_PTR cchLen = 0;
137 MSIHANDLE hTable = NULL;
138 MSIHANDLE hColumns = NULL;
139
140 hr = WcaInitialize(hInstall, "WixRemoveFoldersEx");
141 ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx.");
142
143 WcaInitializeWow64();
144
145 // anything to do?
146 if (S_OK != WcaTableExists(L"Wix4RemoveFolderEx"))
147 {
148 WcaLog(LOGMSG_STANDARD, "Wix4RemoveFolderEx table doesn't exist, so there are no folders to remove.");
149 ExitFunction();
150 }
151
152 // query and loop through all the remove folders exceptions
153 hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView);
154 ExitOnFailure(hr, "Failed to open view on Wix4RemoveFolderEx table");
155
156 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
157 {
158 hr = WcaGetRecordString(hRec, rfqId, &sczId);
159 ExitOnFailure(hr, "Failed to get remove folder identity.");
160
161 hr = WcaGetRecordString(hRec, rfqCondition, &sczCondition);
162 ExitOnFailure(hr, "Failed to get remove folder condition.");
163
164 if (sczCondition && *sczCondition)
165 {
166 MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
167 if (MSICONDITION_TRUE == condition)
168 {
169 WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
170 }
171 else
172 {
173 WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
174 continue;
175 }
176 }
177
178 hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent);
179 ExitOnFailure(hr, "Failed to get remove folder component.");
180
181 hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty);
182 ExitOnFailure(hr, "Failed to get remove folder property.");
183
184 hr = WcaGetRecordInteger(hRec, rfqMode, &iMode);
185 ExitOnFailure(hr, "Failed to get remove folder mode");
186
187 hr = WcaGetProperty(sczProperty, &sczPath);
188 ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId);
189
190 hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes);
191 ExitOnFailure(hr, "failed to get component attributes for row: %ls", sczId);
192
193 f64BitComponent = iComponentAttributes & msidbComponentAttributes64bit;
194
195 // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder
196 // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null
197 hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen));
198 if (SUCCEEDED(hr))
199 {
200 ExitOnFailure(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId);
201 }
202
203 hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT);
204 ExitOnFailure(hr, "Failed to expand path: %S for row: %S", sczPath, sczId);
205
206 hr = PathBackslashTerminate(&sczExpandedPath);
207 ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath);
208
209 WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId);
210 hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns);
211 ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId);
212 }
213
214 // reaching the end of the list is actually a good thing, not an error
215 if (E_NOMOREITEMS == hr)
216 {
217 hr = S_OK;
218 }
219 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveFolderEx table");
220
221LExit:
222 WcaFinalizeWow64();
223
224 if (hColumns)
225 {
226 ::MsiCloseHandle(hColumns);
227 }
228
229 if (hTable)
230 {
231 ::MsiCloseHandle(hTable);
232 }
233
234 ReleaseStr(sczExpandedPath);
235 ReleaseStr(sczPath);
236 ReleaseStr(sczProperty);
237 ReleaseStr(sczComponent);
238 ReleaseStr(sczCondition);
239 ReleaseStr(sczId);
240
241 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
242 return WcaFinalize(er);
243}