diff options
Diffstat (limited to 'src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp')
-rw-r--r-- | src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp new file mode 100644 index 00000000..a7e98491 --- /dev/null +++ b/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp | |||
@@ -0,0 +1,429 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | static HRESULT CaScriptFileName( | ||
7 | __in WCA_ACTION action, | ||
8 | __in WCA_CASCRIPT script, | ||
9 | __in BOOL fImpersonated, | ||
10 | __in_z LPCWSTR wzScriptKey, | ||
11 | __out LPWSTR* pwzScriptName | ||
12 | ); | ||
13 | |||
14 | |||
15 | /******************************************************************** | ||
16 | WcaCaScriptCreateKey() - creates a unique script key for this | ||
17 | CustomAction. | ||
18 | |||
19 | ********************************************************************/ | ||
20 | extern "C" HRESULT WIXAPI WcaCaScriptCreateKey( | ||
21 | __out LPWSTR* ppwzScriptKey | ||
22 | ) | ||
23 | { | ||
24 | AssertSz(WcaIsInitialized(), "WcaInitialize() should have been called before calling this function."); | ||
25 | HRESULT hr = S_OK; | ||
26 | |||
27 | hr = StrAllocStringAnsi(ppwzScriptKey, WcaGetLogName(), 0, CP_ACP); | ||
28 | ExitOnFailure(hr, "Failed to create script key."); | ||
29 | |||
30 | LExit: | ||
31 | return hr; | ||
32 | } | ||
33 | |||
34 | |||
35 | /******************************************************************** | ||
36 | WcaCaScriptCreate() - creates the appropriate script for this | ||
37 | CustomAction Script Key. | ||
38 | |||
39 | ********************************************************************/ | ||
40 | extern "C" HRESULT WIXAPI WcaCaScriptCreate( | ||
41 | __in WCA_ACTION action, | ||
42 | __in WCA_CASCRIPT script, | ||
43 | __in BOOL fImpersonated, | ||
44 | __in_z LPCWSTR wzScriptKey, | ||
45 | __in BOOL fAppend, | ||
46 | __out WCA_CASCRIPT_HANDLE* phScript | ||
47 | ) | ||
48 | { | ||
49 | HRESULT hr = S_OK; | ||
50 | LPWSTR pwzScriptPath = NULL; | ||
51 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
52 | |||
53 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
54 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
55 | |||
56 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
57 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
58 | { | ||
59 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
60 | } | ||
61 | |||
62 | if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END)) | ||
63 | { | ||
64 | ExitWithLastError(hr, "Failed to seek to end of file."); | ||
65 | } | ||
66 | |||
67 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
68 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
69 | |||
70 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
71 | pwzScriptPath = NULL; | ||
72 | (*phScript)->hScriptFile = hScriptFile; | ||
73 | hScriptFile = INVALID_HANDLE_VALUE; | ||
74 | |||
75 | LExit: | ||
76 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
77 | { | ||
78 | ::CloseHandle(hScriptFile); | ||
79 | } | ||
80 | |||
81 | ReleaseStr(pwzScriptPath); | ||
82 | return hr; | ||
83 | } | ||
84 | |||
85 | |||
86 | /******************************************************************** | ||
87 | WcaCaScriptOpen() - opens the appropriate script for this CustomAction | ||
88 | Script Key. | ||
89 | |||
90 | ********************************************************************/ | ||
91 | extern "C" HRESULT WIXAPI WcaCaScriptOpen( | ||
92 | __in WCA_ACTION action, | ||
93 | __in WCA_CASCRIPT script, | ||
94 | __in BOOL fImpersonated, | ||
95 | __in_z LPCWSTR wzScriptKey, | ||
96 | __out WCA_CASCRIPT_HANDLE* phScript | ||
97 | ) | ||
98 | { | ||
99 | HRESULT hr = S_OK; | ||
100 | LPWSTR pwzScriptPath = NULL; | ||
101 | HANDLE hScriptFile = INVALID_HANDLE_VALUE; | ||
102 | |||
103 | hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath); | ||
104 | ExitOnFailure(hr, "Failed to calculate script file name."); | ||
105 | |||
106 | hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
107 | if (INVALID_HANDLE_VALUE == hScriptFile) | ||
108 | { | ||
109 | ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath); | ||
110 | } | ||
111 | |||
112 | *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE)); | ||
113 | ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle."); | ||
114 | |||
115 | (*phScript)->pwzScriptPath = pwzScriptPath; | ||
116 | pwzScriptPath = NULL; | ||
117 | (*phScript)->hScriptFile = hScriptFile; | ||
118 | hScriptFile = INVALID_HANDLE_VALUE; | ||
119 | |||
120 | LExit: | ||
121 | if (INVALID_HANDLE_VALUE != hScriptFile) | ||
122 | { | ||
123 | ::CloseHandle(hScriptFile); | ||
124 | } | ||
125 | |||
126 | ReleaseStr(pwzScriptPath); | ||
127 | return hr; | ||
128 | } | ||
129 | |||
130 | |||
131 | /******************************************************************** | ||
132 | WcaCaScriptClose() - closes an open script handle. | ||
133 | |||
134 | ********************************************************************/ | ||
135 | extern "C" void WIXAPI WcaCaScriptClose( | ||
136 | __in_opt WCA_CASCRIPT_HANDLE hScript, | ||
137 | __in WCA_CASCRIPT_CLOSE closeOperation | ||
138 | ) | ||
139 | { | ||
140 | if (hScript) | ||
141 | { | ||
142 | if (INVALID_HANDLE_VALUE != hScript->hScriptFile) | ||
143 | { | ||
144 | ::CloseHandle(hScript->hScriptFile); | ||
145 | } | ||
146 | |||
147 | if (hScript->pwzScriptPath) | ||
148 | { | ||
149 | if (WCA_CASCRIPT_CLOSE_DELETE == closeOperation) | ||
150 | { | ||
151 | ::DeleteFileW(hScript->pwzScriptPath); | ||
152 | } | ||
153 | |||
154 | StrFree(hScript->pwzScriptPath); | ||
155 | } | ||
156 | |||
157 | MemFree(hScript); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | |||
162 | /******************************************************************** | ||
163 | WcaCaScriptReadAsCustomActionData() - read the ca script into a format | ||
164 | that is useable by other CA data | ||
165 | functions. | ||
166 | |||
167 | ********************************************************************/ | ||
168 | extern "C" HRESULT WIXAPI WcaCaScriptReadAsCustomActionData( | ||
169 | __in WCA_CASCRIPT_HANDLE hScript, | ||
170 | __out LPWSTR* ppwzCustomActionData | ||
171 | ) | ||
172 | { | ||
173 | HRESULT hr = S_OK; | ||
174 | LARGE_INTEGER liScriptSize = { 0 }; | ||
175 | BYTE* pbData = NULL; | ||
176 | DWORD cbData = 0; | ||
177 | |||
178 | if (!::GetFileSizeEx(hScript->hScriptFile, &liScriptSize)) | ||
179 | { | ||
180 | ExitWithLastError(hr, "Failed to get size of ca script file."); | ||
181 | } | ||
182 | |||
183 | if (0 != liScriptSize.HighPart || 0 != (liScriptSize.LowPart % sizeof(WCHAR))) | ||
184 | { | ||
185 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); | ||
186 | ExitOnRootFailure(hr, "Invalid data read from ca script."); | ||
187 | } | ||
188 | |||
189 | cbData = liScriptSize.LowPart; | ||
190 | if (cbData) | ||
191 | { | ||
192 | pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); | ||
193 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in ca script."); | ||
194 | |||
195 | if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_BEGIN)) | ||
196 | { | ||
197 | ExitWithLastError(hr, "Failed to reset to beginning of ca script."); | ||
198 | } | ||
199 | |||
200 | DWORD cbTotalRead = 0; | ||
201 | DWORD cbRead = 0; | ||
202 | do | ||
203 | { | ||
204 | if (!::ReadFile(hScript->hScriptFile, pbData + cbTotalRead, cbData - cbTotalRead, &cbRead, NULL)) | ||
205 | { | ||
206 | ExitWithLastError(hr, "Failed to read from ca script."); | ||
207 | } | ||
208 | |||
209 | cbTotalRead += cbRead; | ||
210 | } while (cbRead && cbTotalRead < cbData); | ||
211 | |||
212 | if (cbTotalRead != cbData) | ||
213 | { | ||
214 | hr = E_UNEXPECTED; | ||
215 | ExitOnFailure(hr, "Failed to completely read ca script."); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | // Add one to the allocated space because the data stored in the script is not | ||
220 | // null terminated. After copying the memory over, we'll ensure the string is | ||
221 | // null terminated. | ||
222 | DWORD cchData = cbData / sizeof(WCHAR) + 1; | ||
223 | hr = StrAlloc(ppwzCustomActionData, cchData); | ||
224 | ExitOnFailure(hr, "Failed to copy ca script."); | ||
225 | |||
226 | if (cbData) | ||
227 | { | ||
228 | CopyMemory(*ppwzCustomActionData, pbData, cbData); | ||
229 | } | ||
230 | |||
231 | (*ppwzCustomActionData)[cchData - 1] = L'\0'; | ||
232 | |||
233 | LExit: | ||
234 | ReleaseMem(pbData); | ||
235 | return hr; | ||
236 | } | ||
237 | |||
238 | |||
239 | /******************************************************************** | ||
240 | WcaCaScriptWriteString() - writes a string to the ca script. | ||
241 | |||
242 | ********************************************************************/ | ||
243 | extern "C" HRESULT WIXAPI WcaCaScriptWriteString( | ||
244 | __in WCA_CASCRIPT_HANDLE hScript, | ||
245 | __in_z LPCWSTR wzValue | ||
246 | ) | ||
247 | { | ||
248 | HRESULT hr = S_OK; | ||
249 | DWORD cbFile = 0; | ||
250 | WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator | ||
251 | SIZE_T cch = 0; | ||
252 | |||
253 | cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END); | ||
254 | if (INVALID_SET_FILE_POINTER == cbFile) | ||
255 | { | ||
256 | ExitWithLastError(hr, "Failed to move file pointer to end of file."); | ||
257 | } | ||
258 | |||
259 | // If there is existing data in the file, append on the magic delimeter | ||
260 | // before adding our new data on the end of the file. | ||
261 | if (0 < cbFile) | ||
262 | { | ||
263 | hr = FileWriteHandle(hScript->hScriptFile, reinterpret_cast<LPCBYTE>(delim), sizeof(delim)); | ||
264 | ExitOnFailure(hr, "Failed to write data to ca script."); | ||
265 | } | ||
266 | |||
267 | hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cch)); | ||
268 | ExitOnRootFailure(hr, "Failed to get length of ca script string."); | ||
269 | |||
270 | hr = FileWriteHandle(hScript->hScriptFile, reinterpret_cast<LPCBYTE>(wzValue), static_cast<DWORD>(cch) * sizeof(WCHAR)); | ||
271 | ExitOnFailure(hr, "Failed to write data to ca script."); | ||
272 | |||
273 | LExit: | ||
274 | return hr; | ||
275 | } | ||
276 | |||
277 | |||
278 | /******************************************************************** | ||
279 | WcaCaScriptWriteNumber() - writes a number to the ca script. | ||
280 | |||
281 | ********************************************************************/ | ||
282 | extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber( | ||
283 | __in WCA_CASCRIPT_HANDLE hScript, | ||
284 | __in DWORD dwValue | ||
285 | ) | ||
286 | { | ||
287 | HRESULT hr = S_OK; | ||
288 | WCHAR wzBuffer[13] = { 0 }; | ||
289 | |||
290 | hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue); | ||
291 | ExitOnFailure(hr, "Failed to convert number into string."); | ||
292 | |||
293 | hr = WcaCaScriptWriteString(hScript, wzBuffer); | ||
294 | ExitOnFailure(hr, "Failed to write number to script."); | ||
295 | |||
296 | LExit: | ||
297 | return hr; | ||
298 | } | ||
299 | |||
300 | |||
301 | /******************************************************************** | ||
302 | WcaCaScriptFlush() - best effort function to get script written to | ||
303 | disk. | ||
304 | |||
305 | ********************************************************************/ | ||
306 | extern "C" void WIXAPI WcaCaScriptFlush( | ||
307 | __in WCA_CASCRIPT_HANDLE hScript | ||
308 | ) | ||
309 | { | ||
310 | ::FlushFileBuffers(hScript->hScriptFile); | ||
311 | } | ||
312 | |||
313 | |||
314 | /******************************************************************** | ||
315 | WcaCaScriptCleanup() - best effort clean-up of any cascripts left | ||
316 | over from this install/uninstall. | ||
317 | |||
318 | ********************************************************************/ | ||
319 | extern "C" void WIXAPI WcaCaScriptCleanup( | ||
320 | __in_z LPCWSTR wzProductCode, | ||
321 | __in BOOL fImpersonated | ||
322 | ) | ||
323 | { | ||
324 | HRESULT hr = S_OK; | ||
325 | WCHAR wzTempPath[MAX_PATH]; | ||
326 | LPWSTR pwzWildCardPath = NULL; | ||
327 | WIN32_FIND_DATAW fd = { 0 }; | ||
328 | HANDLE hff = INVALID_HANDLE_VALUE; | ||
329 | LPWSTR pwzDeletePath = NULL; | ||
330 | |||
331 | if (fImpersonated) | ||
332 | { | ||
333 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
334 | { | ||
335 | ExitWithLastError(hr, "Failed to get temp path."); | ||
336 | } | ||
337 | } | ||
338 | else | ||
339 | { | ||
340 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
341 | { | ||
342 | ExitWithLastError(hr, "Failed to get windows path."); | ||
343 | } | ||
344 | |||
345 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
346 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
347 | } | ||
348 | |||
349 | hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode); | ||
350 | ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts."); | ||
351 | |||
352 | hff = ::FindFirstFileW(pwzWildCardPath, &fd); | ||
353 | if (INVALID_HANDLE_VALUE == hff) | ||
354 | { | ||
355 | ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath); | ||
356 | } | ||
357 | |||
358 | do | ||
359 | { | ||
360 | hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName); | ||
361 | if (SUCCEEDED(hr)) | ||
362 | { | ||
363 | if (!::DeleteFileW(pwzDeletePath)) | ||
364 | { | ||
365 | DWORD er = ::GetLastError(); | ||
366 | WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er); | ||
367 | } | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr); | ||
372 | } | ||
373 | } while(::FindNextFileW(hff, &fd)); | ||
374 | |||
375 | LExit: | ||
376 | if (INVALID_HANDLE_VALUE == hff) | ||
377 | { | ||
378 | ::FindClose(hff); | ||
379 | } | ||
380 | |||
381 | ReleaseStr(pwzDeletePath); | ||
382 | ReleaseStr(pwzWildCardPath); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | |||
387 | static HRESULT CaScriptFileName( | ||
388 | __in WCA_ACTION action, | ||
389 | __in WCA_CASCRIPT script, | ||
390 | __in BOOL fImpersonated, | ||
391 | __in_z LPCWSTR wzScriptKey, | ||
392 | __out LPWSTR* ppwzScriptName | ||
393 | ) | ||
394 | { | ||
395 | HRESULT hr = S_OK; | ||
396 | WCHAR wzTempPath[MAX_PATH]; | ||
397 | LPWSTR pwzProductCode = NULL; | ||
398 | WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u'; | ||
399 | WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r'; | ||
400 | WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm'; | ||
401 | |||
402 | if (fImpersonated) | ||
403 | { | ||
404 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
405 | { | ||
406 | ExitWithLastError(hr, "Failed to get temp path."); | ||
407 | } | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath))) | ||
412 | { | ||
413 | ExitWithLastError(hr, "Failed to get windows path."); | ||
414 | } | ||
415 | |||
416 | hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\"); | ||
417 | ExitOnFailure(hr, "Failed to concat Installer directory on windows path string."); | ||
418 | } | ||
419 | |||
420 | hr = WcaGetProperty(L"ProductCode", &pwzProductCode); | ||
421 | ExitOnFailure(hr, "Failed to get ProductCode."); | ||
422 | |||
423 | hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall); | ||
424 | ExitOnFailure(hr, "Failed to allocate path to ca script."); | ||
425 | |||
426 | LExit: | ||
427 | ReleaseStr(pwzProductCode); | ||
428 | return hr; | ||
429 | } | ||