main
cpp 1,125 lines 38.3 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 #define XMLCONFIG_ELEMENT 0x00000001
6 #define XMLCONFIG_VALUE 0x00000002
7 #define XMLCONFIG_DOCUMENT 0x00000004
8 #define XMLCONFIG_CREATE 0x00000010
9 #define XMLCONFIG_DELETE 0x00000020
10 #define XMLCONFIG_INSTALL 0x00000100
11 #define XMLCONFIG_UNINSTALL 0x00000200
12 #define XMLCONFIG_PRESERVE_MODIFIED 0x00001000
13
14 enum eXmlAction
15 {
16 xaUnknown = 0,
17 xaOpenFile,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaWriteDocument,
21 xaDeleteValue,
22 xaCreateElement,
23 xaDeleteElement,
24 };
25
26 enum eXmlPreserveDate
27 {
28 xdDontPreserve = 0,
29 xdPreserve
30 };
31
32 LPCWSTR vcsXmlConfigQuery =
33 L"SELECT `Wix4XmlConfig`.`XmlConfig`, `Wix4XmlConfig`.`File`, `Wix4XmlConfig`.`ElementId`, `Wix4XmlConfig`.`ElementPath`, `Wix4XmlConfig`.`VerifyPath`, `Wix4XmlConfig`.`Name`, "
34 L"`Wix4XmlConfig`.`Value`, `Wix4XmlConfig`.`Flags`, `Wix4XmlConfig`.`Component_`, `Component`.`Attributes` "
35 L"FROM `Wix4XmlConfig`,`Component` WHERE `Wix4XmlConfig`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
36 enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, xfqElementId, xfqElementPath, xfqVerifyPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
37
38 struct XML_CONFIG_CHANGE
39 {
40 WCHAR wzId[MAX_DARWIN_KEY + 1];
41
42 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
43 INSTALLSTATE isInstalled;
44 INSTALLSTATE isAction;
45
46 WCHAR wzFile[MAX_PATH];
47 LPWSTR pwzElementId;
48 LPWSTR pwzElementPath;
49 LPWSTR pwzVerifyPath;
50 WCHAR wzName[MAX_DARWIN_COLUMN];
51 LPWSTR pwzValue;
52 BOOL fInstalledFile;
53
54 int iXmlFlags;
55 int iCompAttributes;
56
57 XML_CONFIG_CHANGE* pxfcAdditionalChanges;
58 int cAdditionalChanges;
59
60 XML_CONFIG_CHANGE* pxfcPrev;
61 XML_CONFIG_CHANGE* pxfcNext;
62 };
63
64 static HRESULT FreeXmlConfigChangeList(
65 __in_opt XML_CONFIG_CHANGE* pxfcList
66 )
67 {
68 HRESULT hr = S_OK;
69
70 XML_CONFIG_CHANGE* pxfcDelete;
71 while(pxfcList)
72 {
73 pxfcDelete = pxfcList;
74 pxfcList = pxfcList->pxfcNext;
75
76 if (pxfcDelete->pwzElementId)
77 {
78 hr = MemFree(pxfcDelete->pwzElementId);
79 ExitOnFailure(hr, "failed to free xml config element id in change list item");
80 }
81
82 if (pxfcDelete->pwzElementPath)
83 {
84 hr = MemFree(pxfcDelete->pwzElementPath);
85 ExitOnFailure(hr, "failed to free xml config element path in change list item");
86 }
87
88 if (pxfcDelete->pwzVerifyPath)
89 {
90 hr = MemFree(pxfcDelete->pwzVerifyPath);
91 ExitOnFailure(hr, "failed to free xml config verify path in change list item");
92 }
93
94 if (pxfcDelete->pwzValue)
95 {
96 hr = MemFree(pxfcDelete->pwzValue);
97 ExitOnFailure(hr, "failed to free xml config value in change list item");
98 }
99
100 hr = MemFree(pxfcDelete);
101 ExitOnFailure(hr, "failed to free xml config change list item");
102 }
103
104 LExit:
105 return hr;
106 }
107
108 static HRESULT AddXmlConfigChangeToList(
109 __inout XML_CONFIG_CHANGE** ppxfcHead,
110 __inout XML_CONFIG_CHANGE** ppxfcTail
111 )
112 {
113 Assert(ppxfcHead && ppxfcTail);
114
115 HRESULT hr = S_OK;
116
117 XML_CONFIG_CHANGE* pxfc = static_cast<XML_CONFIG_CHANGE*>(MemAlloc(sizeof(XML_CONFIG_CHANGE), TRUE));
118 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
119
120 // Add it to the end of the list
121 if (NULL == *ppxfcHead)
122 {
123 *ppxfcHead = pxfc;
124 *ppxfcTail = pxfc;
125 }
126 else
127 {
128 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
129 (*ppxfcTail)->pxfcNext = pxfc;
130 pxfc->pxfcPrev = *ppxfcTail;
131 *ppxfcTail = pxfc;
132 }
133
134 LExit:
135 return hr;
136 }
137
138
139 static HRESULT ReadXmlConfigTable(
140 __inout XML_CONFIG_CHANGE** ppxfcHead,
141 __inout XML_CONFIG_CHANGE** ppxfcTail
142 )
143 {
144 Assert(ppxfcHead && ppxfcTail);
145
146 HRESULT hr = S_OK;
147 UINT er = ERROR_SUCCESS;
148
149 PMSIHANDLE hView = NULL;
150 PMSIHANDLE hRec = NULL;
151
152 LPWSTR pwzData = NULL;
153
154 // loop through all the xml configurations
155 hr = WcaOpenExecuteView(vcsXmlConfigQuery, &hView);
156 ExitOnFailure(hr, "failed to open view on Wix4XmlConfig table");
157
158 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
159 {
160 hr = AddXmlConfigChangeToList(ppxfcHead, ppxfcTail);
161 ExitOnFailure(hr, "failed to add xml file change to list");
162
163 // Get record Id
164 hr = WcaGetRecordString(hRec, xfqXmlConfig, &pwzData);
165 ExitOnFailure(hr, "failed to get Wix4XmlConfig record Id");
166 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
167 ExitOnFailure(hr, "failed to copy Wix4XmlConfig record Id");
168
169 // Get component name
170 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
171 ExitOnFailure(hr, "failed to get component name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
172
173 // Get the component's state
174 if (pwzData && *pwzData)
175 {
176 hr = StringCchCopyW((*ppxfcTail)->wzComponent, countof((*ppxfcTail)->wzComponent), pwzData);
177 ExitOnFailure(hr, "failed to copy component id");
178
179 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), (*ppxfcTail)->wzComponent, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
180 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for component id");
181 }
182
183 // Get the xml file
184 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
185 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
186 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
187 ExitOnFailure(hr, "failed to copy xml file path");
188
189 // Figure out if the file is already on the machine or if it's being installed
190 hr = WcaGetRecordString(hRec, xfqFile, &pwzData);
191 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
192 if (NULL != wcsstr(pwzData, L"[!") || NULL != wcsstr(pwzData, L"[#"))
193 {
194 (*ppxfcTail)->fInstalledFile = TRUE;
195 }
196
197 // Get the Wix4XmlConfig table flags
198 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
199 ExitOnFailure(hr, "failed to get Wix4XmlConfig flags for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
200
201 // Get the Element Id
202 hr = WcaGetRecordFormattedString(hRec, xfqElementId, &(*ppxfcTail)->pwzElementId);
203 ExitOnFailure(hr, "failed to get Element Id for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
204
205 // Get the Element Path
206 hr = WcaGetRecordFormattedString(hRec, xfqElementPath, &(*ppxfcTail)->pwzElementPath);
207 ExitOnFailure(hr, "failed to get Element Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
208
209 // Get the Verify Path
210 hr = WcaGetRecordFormattedString(hRec, xfqVerifyPath, &(*ppxfcTail)->pwzVerifyPath);
211 ExitOnFailure(hr, "failed to get Verify Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
212
213 // Get the name
214 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
215 ExitOnFailure(hr, "failed to get Name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
216 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
217 ExitOnFailure(hr, "failed to copy name of element");
218
219 // Get the value
220 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
221 ExitOnFailure(hr, "failed to get Value for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
222 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
223 ExitOnFailure(hr, "failed to allocate buffer for value");
224
225 // Get the component attributes
226 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
227 ExitOnFailure(hr, "failed to get component attributes for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
228 }
229
230 // if we looped through all records all is well
231 if (E_NOMOREITEMS == hr)
232 {
233 hr = S_OK;
234 }
235 ExitOnFailure(hr, "failed while looping through all objects to secure");
236
237 LExit:
238 ReleaseStr(pwzData);
239
240 return hr;
241 }
242
243 static HRESULT ProcessChanges(
244 __inout XML_CONFIG_CHANGE** ppxfcHead
245 )
246 {
247 Assert(ppxfcHead && *ppxfcHead);
248 HRESULT hr = S_OK;
249
250 XML_CONFIG_CHANGE* pxfc = NULL;
251 XML_CONFIG_CHANGE* pxfcNext = NULL;
252 XML_CONFIG_CHANGE* pxfcCheck = NULL;
253 int cAdditionalChanges = 0;
254 XML_CONFIG_CHANGE* pxfcLast = NULL;
255
256 // If there's only one item in the list, none of this matters
257 if (pxfc && !pxfc->pxfcNext)
258 {
259 ExitFunction();
260 }
261
262 // Loop through the list
263 pxfc = *ppxfcHead;
264 while (pxfc)
265 {
266 // Keep track of where our next spot will be since our current node may be moved
267 pxfcNext = pxfc->pxfcNext;
268
269 // With each node, check to see if its element path matches the Id of some other node in the list
270 pxfcCheck = *ppxfcHead;
271 while (pxfcCheck)
272 {
273 if (pxfc->pwzElementId && *pxfc->pwzElementId)
274 {
275 if (0 == lstrcmpW(pxfc->pwzElementId, pxfcCheck->wzId)
276 && 0 == pxfc->iXmlFlags
277 && XMLCONFIG_CREATE & pxfcCheck->iXmlFlags
278 && XMLCONFIG_ELEMENT & pxfcCheck->iXmlFlags)
279 {
280 // We found a match. First, take it out of the current list
281 if (pxfc->pxfcPrev)
282 {
283 pxfc->pxfcPrev->pxfcNext = pxfc->pxfcNext;
284 }
285 else // it was the head. Update the head
286 {
287 *ppxfcHead = pxfc->pxfcNext;
288 }
289
290 if (pxfc->pxfcNext)
291 {
292 pxfc->pxfcNext->pxfcPrev = pxfc->pxfcPrev;
293 }
294
295 pxfc->pxfcNext = NULL;
296 pxfc->pxfcPrev = NULL;
297
298 // Now, add this node to the end of the matched node's additional changes list
299 if (!pxfcCheck->pxfcAdditionalChanges)
300 {
301 pxfcCheck->pxfcAdditionalChanges = pxfc;
302 pxfcCheck->cAdditionalChanges = 1;
303 }
304 else
305 {
306 pxfcLast = pxfcCheck->pxfcAdditionalChanges;
307 cAdditionalChanges = 1;
308 while (pxfcLast->pxfcNext)
309 {
310 pxfcLast = pxfcLast->pxfcNext;
311 ++cAdditionalChanges;
312 }
313 pxfcLast->pxfcNext = pxfc;
314 pxfc->pxfcPrev = pxfcLast;
315 pxfcCheck->cAdditionalChanges = ++cAdditionalChanges;
316 }
317 }
318 }
319
320 pxfcCheck = pxfcCheck->pxfcNext;
321 }
322
323 pxfc = pxfcNext;
324 }
325
326 LExit:
327
328 return hr;
329 }
330
331
332 static HRESULT BeginChangeFile(
333 __in LPCWSTR pwzFile,
334 __in int iCompAttributes,
335 __inout LPWSTR* ppwzCustomActionData
336 )
337 {
338 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
339
340 HRESULT hr = S_OK;
341 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
342
343 LPBYTE pbData = NULL;
344 SIZE_T cbData = 0;
345
346 LPWSTR pwzRollbackCustomActionData = NULL;
347
348 if (fIs64Bit)
349 {
350 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
351 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
352 }
353 else
354 {
355 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
356 ExitOnFailure(hr, "failed to write file indicator to custom action data");
357 }
358
359 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
360 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
361
362 // If the file already exits, then we have to put it back the way it was on failure
363 if (FileExistsEx(pwzFile, NULL))
364 {
365 hr = FileRead(&pbData, &cbData, pwzFile);
366 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
367
368 // Set up the rollback for this file
369 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
370 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
371
372 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
373 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
374
375 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
376 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
377
378 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
379 ExitOnFailure(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile);
380
381 ReleaseStr(pwzRollbackCustomActionData);
382 }
383 LExit:
384 ReleaseMem(pbData);
385
386 return hr;
387 }
388
389
390 static HRESULT WriteChangeData(
391 __in XML_CONFIG_CHANGE* pxfc,
392 __in eXmlAction action,
393 __inout LPWSTR* ppwzCustomActionData
394 )
395 {
396 Assert(pxfc && ppwzCustomActionData);
397
398 HRESULT hr = S_OK;
399 XML_CONFIG_CHANGE* pxfcAdditionalChanges = NULL;
400 LPCWSTR wzElementPath = pxfc->pwzElementId && *pxfc->pwzElementId ? pxfc->pwzElementId : pxfc->pwzElementPath;
401
402 hr = WcaWriteStringToCaData(wzElementPath, ppwzCustomActionData);
403 ExitOnFailure(hr, "failed to write ElementId/ElementPath to custom action data: %ls", wzElementPath);
404
405 hr = WcaWriteStringToCaData(pxfc->pwzVerifyPath, ppwzCustomActionData);
406 ExitOnFailure(hr, "failed to write VerifyPath to custom action data: %ls", pxfc->pwzVerifyPath);
407
408 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
409 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
410
411 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
412 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
413
414 if (pxfc->iXmlFlags & XMLCONFIG_CREATE && pxfc->iXmlFlags & XMLCONFIG_ELEMENT && xaCreateElement == action && pxfc->pxfcAdditionalChanges)
415 {
416 hr = WcaWriteIntegerToCaData(pxfc->cAdditionalChanges, ppwzCustomActionData);
417 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
418
419 pxfcAdditionalChanges = pxfc->pxfcAdditionalChanges;
420 while (pxfcAdditionalChanges)
421 {
422 Assert((0 == lstrcmpW(pxfcAdditionalChanges->wzComponent, pxfc->wzComponent)) && 0 == pxfcAdditionalChanges->iXmlFlags && (0 == lstrcmpW(pxfcAdditionalChanges->wzFile, pxfc->wzFile)));
423
424 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->wzName, ppwzCustomActionData);
425 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
426
427 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->pwzValue, ppwzCustomActionData);
428 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
429
430 pxfcAdditionalChanges = pxfcAdditionalChanges->pxfcNext;
431 }
432 }
433 else
434 {
435 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
436 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
437 }
438
439 LExit:
440 return hr;
441 }
442
443
444 /******************************************************************
445 SchedXmlConfig - entry point for XmlConfig Custom Action
446
447 ********************************************************************/
448 extern "C" UINT __stdcall SchedXmlConfig(
449 __in MSIHANDLE hInstall
450 )
451 {
452 // AssertSz(FALSE, "debug SchedXmlConfig");
453
454 HRESULT hr = S_OK;
455 UINT er = ERROR_SUCCESS;
456
457 LPWSTR pwzCurrentFile = NULL;
458 BOOL fCurrentFileChanged = FALSE;
459
460 PMSIHANDLE hView = NULL;
461 PMSIHANDLE hRec = NULL;
462
463 XML_CONFIG_CHANGE* pxfcHead = NULL;
464 XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more?
465 XML_CONFIG_CHANGE* pxfc = NULL;
466
467 eXmlAction xa = xaUnknown;
468 eXmlPreserveDate xd;
469
470 LPWSTR pwzCustomActionData = NULL;
471
472 DWORD cFiles = 0;
473
474 // initialize
475 hr = WcaInitialize(hInstall, "SchedXmlConfig");
476 ExitOnFailure(hr, "failed to initialize");
477
478 hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail);
479 MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read Wix4XmlConfig table");
480
481 hr = ProcessChanges(&pxfcHead);
482 ExitOnFailure(hr, "failed to process Wix4XmlConfig changes");
483
484 // loop through all the xml configurations
485 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
486 {
487 // If this is a different file, or the first file...
488 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile))
489 {
490 // Remember the file we're currently working on
491 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
492 ExitOnFailure(hr, "failed to copy file name");
493
494 fCurrentFileChanged = TRUE;
495 }
496
497 //
498 // Figure out what action to take
499 //
500 xa = xaUnknown;
501
502 // If it's being installed or reinstalled or uninstalled and that matches
503 // what we are doing then calculate the right action.
504 if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) ||
505 (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction)))
506 {
507 if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
508 {
509 xa = xaCreateElement;
510 }
511 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
512 {
513 xa = xaDeleteElement;
514 }
515 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
516 {
517 xa = xaDeleteValue;
518 }
519 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
520 {
521 xa = xaWriteValue;
522 }
523 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
524 {
525 xa = xaWriteDocument;
526 }
527 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
528 {
529 hr = E_INVALIDARG;
530 ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node.");
531 }
532 }
533
534 if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags)
535 {
536 xd = xdPreserve;
537 }
538 else
539 {
540 xd= xdDontPreserve;
541 }
542
543 if (xaUnknown != xa)
544 {
545 if (fCurrentFileChanged)
546 {
547 hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData);
548 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
549
550 fCurrentFileChanged = FALSE;
551 ++cFiles;
552 }
553
554 hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData);
555 ExitOnFailure(hr, "failed to write action indicator custom action data");
556
557 hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData);
558 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
559
560 hr = WriteChangeData(pxfc, xa, &pwzCustomActionData);
561 ExitOnFailure(hr, "failed to write change data");
562 }
563 }
564
565 // If we looped through all records all is well
566 if (E_NOMOREITEMS == hr)
567 {
568 hr = S_OK;
569 }
570 ExitOnFailure(hr, "failed while looping through all objects to secure");
571
572 // Schedule the custom action and add to progress bar
573 if (pwzCustomActionData && *pwzCustomActionData)
574 {
575 Assert(0 < cFiles);
576
577 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE);
578 ExitOnFailure(hr, "failed to schedule ExecXmlConfig action");
579 }
580
581 LExit:
582 ReleaseStr(pwzCurrentFile);
583 ReleaseStr(pwzCustomActionData);
584
585 FreeXmlConfigChangeList(pxfcHead);
586
587 if (FAILED(hr))
588 {
589 er = ERROR_INSTALL_FAILURE;
590 }
591 return WcaFinalize(er);
592 }
593
594
595 /******************************************************************
596 ExecXmlConfig - entry point for XmlConfig Custom Action
597
598 *******************************************************************/
599 extern "C" UINT __stdcall ExecXmlConfig(
600 __in MSIHANDLE hInstall
601 )
602 {
603 //AssertSz(FALSE, "debug ExecXmlConfig");
604 HRESULT hr = S_OK;
605 HRESULT hrOpenFailure = S_OK;
606 UINT er = ERROR_SUCCESS;
607
608 #ifndef _WIN64
609 BOOL fIsFSRedirectDisabled = FALSE;
610 #endif
611 BOOL fPreserveDate = FALSE;
612
613 LPWSTR pwzCustomActionData = NULL;
614 LPWSTR pwzData = NULL;
615 LPWSTR pwzFile = NULL;
616 LPWSTR pwzElementPath = NULL;
617 LPWSTR pwzVerifyPath = NULL;
618 LPWSTR pwzName = NULL;
619 LPWSTR pwzValue = NULL;
620 LPWSTR pwz = NULL;
621 int cAdditionalChanges = 0;
622
623 IXMLDOMDocument* pixd = NULL;
624 IXMLDOMNode* pixn = NULL;
625 IXMLDOMNode* pixnVerify = NULL;
626 IXMLDOMNode* pixnNewNode = NULL;
627 IXMLDOMNode* pixnRemovedChild = NULL;
628
629 IXMLDOMDocument* pixdNew = NULL;
630 IXMLDOMElement* pixeNew = NULL;
631
632 FILETIME ft;
633
634 int id = IDRETRY;
635
636 eXmlAction xa;
637 eXmlPreserveDate xd;
638
639 // initialize
640 hr = WcaInitialize(hInstall, "ExecXmlConfig");
641 ExitOnFailure(hr, "failed to initialize");
642
643 hr = XmlInitialize();
644 ExitOnFailure(hr, "failed to initialize xml utilities");
645
646 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
647 ExitOnFailure(hr, "failed to get CustomActionData");
648
649 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
650
651 pwz = pwzCustomActionData;
652
653 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
654 ExitOnFailure(hr, "failed to process CustomActionData");
655
656 #ifndef _WIN64
657 // Initialize the Wow64 API - store the result in fWow64APIPresent
658 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
659 WcaInitializeWow64();
660 BOOL fIsWow64Process = WcaIsWow64Process();
661 #endif
662
663 if (xaOpenFile != xa && xaOpenFilex64 != xa)
664 {
665 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
666 }
667
668 // loop through all the passed in data
669 while (pwz && *pwz)
670 {
671 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
672 ExitOnFailure(hr, "failed to read file name from custom action data");
673
674 // Default to not preserve date, preserve it if any modifications require us to
675 fPreserveDate = FALSE;
676
677 // Open the file
678 ReleaseNullObject(pixd);
679
680 #ifndef _WIN64
681 if (xaOpenFilex64 == xa)
682 {
683 if (!fIsWow64Process)
684 {
685 hr = E_NOTIMPL;
686 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
687 }
688
689 hr = WcaDisableWow64FSRedirection();
690 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
691
692 fIsFSRedirectDisabled = TRUE;
693 }
694 #endif
695
696 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
697 if (FAILED(hr))
698 {
699 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
700 hrOpenFailure = hr;
701 hr = S_OK;
702 }
703 else
704 {
705 hrOpenFailure = S_OK;
706 }
707
708 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
709
710 while (pwz && *pwz)
711 {
712 // If we skip past an element that has additional changes we need to strip them off the stream before
713 // moving on to the next element. Do that now and then restart the outer loop.
714 if (cAdditionalChanges > 0)
715 {
716 while (cAdditionalChanges > 0)
717 {
718 hr = WcaReadStringFromCaData(&pwz, &pwzName);
719 ExitOnFailure(hr, "failed to process CustomActionData");
720 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
721 ExitOnFailure(hr, "failed to process CustomActionData");
722
723 cAdditionalChanges--;
724 }
725 continue;
726 }
727
728 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
729 ExitOnFailure(hr, "failed to process CustomActionData");
730
731 // Break if we need to move on to a different file
732 if (xaOpenFile == xa || xaOpenFilex64 == xa)
733 {
734 break;
735 }
736
737 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
738 ExitOnFailure(hr, "failed to process CustomActionData");
739
740 if (xdPreserve == xd)
741 {
742 fPreserveDate = TRUE;
743 }
744
745 // Get path, name, and value to be written
746 hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
747 ExitOnFailure(hr, "failed to process CustomActionData");
748 hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
749 ExitOnFailure(hr, "failed to process CustomActionData");
750 hr = WcaReadStringFromCaData(&pwz, &pwzName);
751 ExitOnFailure(hr, "failed to process CustomActionData");
752 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
753 ExitOnFailure(hr, "failed to process CustomActionData");
754 hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
755 ExitOnFailure(hr, "failed to process CustomActionData");
756
757 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
758 if (FAILED(hrOpenFailure))
759 {
760 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
761 {
762 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
763 }
764 else
765 {
766 continue;
767 }
768 }
769
770 // Select the node we're about to modify
771 ReleaseNullObject(pixn);
772
773 hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);
774
775 // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
776 if (S_FALSE == hr)
777 {
778 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
779 {
780 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
781 }
782 else
783 {
784 hr = S_OK;
785 continue;
786 }
787 }
788
789 MessageExitOnFailure(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);
790
791 // Make the modification
792 switch (xa)
793 {
794 case xaWriteValue:
795 if (pwzName && *pwzName)
796 {
797 // We're setting an attribute
798 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
799 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
800 }
801 else
802 {
803 // We're setting the text of the node
804 hr = XmlSetText(pixn, pwzValue);
805 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath);
806 }
807 break;
808 case xaWriteDocument:
809 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
810 {
811 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
812 if (S_OK == hr)
813 {
814 // We found the verify path which means we have no further work to do
815 continue;
816 }
817 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
818 }
819
820 hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
821 ExitOnFailure(hr, "Failed to load value as document.");
822
823 hr = pixdNew->get_documentElement(&pixeNew);
824 ExitOnFailure(hr, "Failed to get document element.");
825
826 hr = pixn->appendChild(pixeNew, NULL);
827 ExitOnFailure(hr, "Failed to append document element on to parent element.");
828
829 ReleaseNullObject(pixeNew);
830 ReleaseNullObject(pixdNew);
831 break;
832
833 case xaCreateElement:
834 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
835 {
836 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
837 if (S_OK == hr)
838 {
839 // We found the verify path which means we have no further work to do
840 continue;
841 }
842 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
843 }
844
845 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
846 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
847
848 if (pwzValue && *pwzValue)
849 {
850 hr = XmlSetText(pixnNewNode, pwzValue);
851 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
852 }
853
854 while (cAdditionalChanges > 0)
855 {
856 hr = WcaReadStringFromCaData(&pwz, &pwzName);
857 ExitOnFailure(hr, "failed to process CustomActionData");
858 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
859 ExitOnFailure(hr, "failed to process CustomActionData");
860
861 // Set the additional attribute
862 hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
863 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
864
865 cAdditionalChanges--;
866 }
867
868 ReleaseNullObject(pixnNewNode);
869 break;
870 case xaDeleteValue:
871 if (pwzName && *pwzName)
872 {
873 // Delete the attribute
874 hr = XmlRemoveAttribute(pixn, pwzName);
875 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
876 }
877 else
878 {
879 // Clear the text value for the node
880 hr = XmlSetText(pixn, L"");
881 ExitOnFailure(hr, "failed to clear text value");
882 }
883 break;
884 case xaDeleteElement:
885 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
886 {
887 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
888 if (S_OK == hr)
889 {
890 hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
891 ExitOnFailure(hr, "failed to remove created child element");
892
893 ReleaseNullObject(pixnRemovedChild);
894 }
895 else
896 {
897 WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath);
898 hr = S_OK;
899 }
900 }
901 else
902 {
903 // TODO: This requires a VerifyPath to delete an element. Should we support not having one?
904 WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
905 }
906 break;
907 default:
908 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
909 break;
910 }
911 }
912
913
914 // Now that we've made all of the changes to this file, save it and move on to the next
915 if (S_OK == hrOpenFailure)
916 {
917 if (fPreserveDate)
918 {
919 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
920 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
921 }
922
923 int iSaveAttempt = 0;
924
925 do
926 {
927 hr = XmlSaveDocument(pixd, pwzFile);
928 if (FAILED(hr))
929 {
930 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
931 switch (id)
932 {
933 case IDABORT:
934 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
935 case IDRETRY:
936 hr = S_FALSE; // hit me, baby, one more time
937 break;
938 case IDIGNORE:
939 hr = S_OK; // pretend everything is okay and bail
940 break;
941 case 0: // No UI case, MsiProcessMessage returns 0
942 if (STIERR_SHARING_VIOLATION == hr)
943 {
944 // Only in case of sharing violation do we retry 30 times, once a second.
945 if (iSaveAttempt < 30)
946 {
947 hr = S_FALSE;
948 ++iSaveAttempt;
949 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
950 Sleep(1000);
951 }
952 else
953 {
954 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
955 }
956 }
957 break;
958 default: // Unknown error
959 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
960 }
961 }
962 } while (S_FALSE == hr);
963
964 if (fPreserveDate)
965 {
966 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
967 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
968 }
969
970 #ifndef _WIN64
971 if (fIsFSRedirectDisabled)
972 {
973 fIsFSRedirectDisabled = FALSE;
974 WcaRevertWow64FSRedirection();
975 }
976 #endif
977 }
978 }
979
980 LExit:
981 #ifndef _WIN64
982 // Make sure we revert FS Redirection if necessary before exiting
983 if (fIsFSRedirectDisabled)
984 {
985 fIsFSRedirectDisabled = FALSE;
986 WcaRevertWow64FSRedirection();
987 }
988 WcaFinalizeWow64();
989 #endif
990
991 ReleaseStr(pwzCustomActionData);
992 ReleaseStr(pwzData);
993 ReleaseStr(pwzFile);
994 ReleaseStr(pwzElementPath);
995 ReleaseStr(pwzVerifyPath);
996 ReleaseStr(pwzName);
997 ReleaseStr(pwzValue);
998
999 ReleaseObject(pixeNew);
1000 ReleaseObject(pixdNew);
1001
1002 ReleaseObject(pixn);
1003 ReleaseObject(pixd);
1004 ReleaseObject(pixnNewNode);
1005 ReleaseObject(pixnRemovedChild);
1006
1007 XmlUninitialize();
1008
1009 if (FAILED(hr))
1010 {
1011 er = ERROR_INSTALL_FAILURE;
1012 }
1013 return WcaFinalize(er);
1014 }
1015
1016
1017 /******************************************************************
1018 ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action
1019
1020 *******************************************************************/
1021 extern "C" UINT __stdcall ExecXmlConfigRollback(
1022 __in MSIHANDLE hInstall
1023 )
1024 {
1025 // AssertSz(FALSE, "debug ExecXmlConfigRollback");
1026 HRESULT hr = S_OK;
1027 UINT er = ERROR_SUCCESS;
1028
1029 int iIs64Bit;
1030 #ifndef _WIN64
1031 BOOL fIs64Bit = FALSE;
1032 #endif
1033
1034 LPWSTR pwzCustomActionData = NULL;
1035 LPWSTR pwz = NULL;
1036 LPWSTR pwzFileName = NULL;
1037 LPBYTE pbData = NULL;
1038 DWORD_PTR cbData = 0;
1039
1040 FILETIME ft;
1041
1042 HANDLE hFile = INVALID_HANDLE_VALUE;
1043
1044 // initialize
1045 hr = WcaInitialize(hInstall, "ExecXmlConfigRollback");
1046 ExitOnFailure(hr, "failed to initialize");
1047
1048
1049 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
1050 ExitOnFailure(hr, "failed to get CustomActionData");
1051
1052 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
1053
1054 pwz = pwzCustomActionData;
1055
1056 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
1057 ExitOnFailure(hr, "failed to read component bitness from custom action data");
1058
1059 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
1060 ExitOnFailure(hr, "failed to read file name from custom action data");
1061
1062 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
1063 ExitOnFailure(hr, "failed to read file contents from custom action data");
1064
1065 #ifndef _WIN64
1066 fIs64Bit = (BOOL)iIs64Bit;
1067
1068 if (fIs64Bit)
1069 {
1070 hr = WcaInitializeWow64();
1071 if (S_FALSE == hr)
1072 {
1073 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
1074 }
1075 ExitOnFailure(hr, "failed to initialize Wow64 API");
1076
1077 if (!WcaIsWow64Process())
1078 {
1079 hr = E_NOTIMPL;
1080 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable.");
1081 }
1082
1083 hr = WcaDisableWow64FSRedirection();
1084 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
1085 }
1086 #endif
1087
1088 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
1089 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
1090
1091 // Open the file
1092 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
1093 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
1094
1095 // Write out the old data
1096 hr = FileWriteHandle(hFile, pbData, cbData);
1097 ExitOnFailure(hr, "failed to write to file: %ls", pwzFileName);
1098
1099 ReleaseFile(hFile);
1100
1101 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
1102 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
1103
1104 LExit:
1105 ReleaseStr(pwzCustomActionData);
1106 ReleaseStr(pwzFileName);
1107
1108 ReleaseFile(hFile);
1109
1110 #ifndef _WIN64
1111 if (fIs64Bit)
1112 {
1113 WcaRevertWow64FSRedirection();
1114 WcaFinalizeWow64();
1115 }
1116 #endif
1117
1118 ReleaseMem(pbData);
1119
1120 if (FAILED(hr))
1121 {
1122 er = ERROR_INSTALL_FAILURE;
1123 }
1124 return WcaFinalize(er);
1125 }