Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2233 lines
64 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. setupasr.c
  5. Abstract:
  6. Services in this module implement the Automatic System Recovery (ASR)
  7. routines of guimode setup.
  8. Revision History:
  9. Initial Code Michael Peterson (v-michpe) 20.Jan.1998
  10. Code cleanup and changes Guhan Suriyanarayanan (guhans) 21.Sep.1999
  11. --*/
  12. #include "setupp.h"
  13. #pragma hdrstop
  14. #include <setupapi.h>
  15. #include <mountmgr.h>
  16. #include <accctrl.h>
  17. #include <aclapi.h>
  18. #define THIS_MODULE 'S'
  19. #include "asrpriv.h"
  20. ///////////////////////////////////////////////////////////////////////////////
  21. // Private Type and constant declarations
  22. ///////////////////////////////////////////////////////////////////////////////
  23. const PCWSTR AsrSifPath = L"%systemroot%\\repair\\asr.sif\0";
  24. const PCWSTR AsrCommandsSectionName = L"COMMANDS";
  25. const PCWSTR AsrCommandSuffix = L"/sifpath=%systemroot%\\repair\\asr.sif";
  26. const PCWSTR AsrTempDir = L"%systemdrive%\\TEMP";
  27. const PCWSTR AsrLogFileName = L"\\asr.log";
  28. const PCWSTR AsrErrorFileName = L"\\asr.err";
  29. const PCWSTR Asr_ControlAsrRegKey = L"SYSTEM\\CurrentControlSet\\Control\\ASR";
  30. const PCWSTR Asr_LastInstanceRegValue = L"Instance";
  31. //
  32. // The following are to update system and boot partition devices
  33. // in setup.log
  34. //
  35. const PCWSTR Asr_SystemDeviceEnvName = L"%ASR_C_SYSTEM_PARTITION_DEVICE%";
  36. const PCWSTR Asr_SystemDeviceWin32Path = L"\\\\?\\GLOBALROOT%ASR_C_SYSTEM_PARTITION_DEVICE%";
  37. const PCWSTR Asr_WinntDeviceEnvName = L"%ASR_C_WINNT_PARTITION_DEVICE%";
  38. const PCWSTR Asr_SetupLogFilePath = L"%systemroot%\\repair\\setup.log";
  39. const PCWSTR Asr_AsrLogFilePath = L"%systemroot%\\repair\\asr.log";
  40. const PCWSTR Asr_AsrErrorFilePath = L"%systemroot%\\repair\\asr.err";
  41. const PCWSTR Asr_OldAsrErrorFilePath = L"%systemroot%\\repair\\asr.err.old";
  42. const PCWSTR Asr_FatalErrorCommand = L"notepad.exe %systemroot%\\repair\\asr.err";
  43. ///////////////////////////////////////////////////////////////////////////////
  44. // Data global to this module
  45. ///////////////////////////////////////////////////////////////////////////////
  46. BOOL Gbl_IsAsrEnabled = FALSE;
  47. PWSTR Gbl_AsrErrorFilePath = NULL;
  48. PWSTR Gbl_AsrLogFilePath = NULL;
  49. HANDLE Gbl_AsrLogFileHandle = NULL;
  50. HANDLE Gbl_AsrSystemVolumeHandle = NULL;
  51. WCHAR g_szErrorMessage[4196];
  52. ///////////////////////////////////////////////////////////////////////////////
  53. // Macros
  54. ///////////////////////////////////////////////////////////////////////////////
  55. //
  56. // ASR Memory allocation and free wrappers
  57. //
  58. //
  59. // _AsrAlloc
  60. // Macro description:
  61. // ASSERTS first if ptr is non-NULL. The expectation is that
  62. // all ptrs must be initialised to NULL before they are allocated.
  63. // That way, we can catch instances where we try to re-allocate
  64. // memory without freeing first.
  65. //
  66. // IsNullFatal: flag to indicate if mem allocation failures are fatal
  67. //
  68. #define _AsrAlloc(ptr,sz,IsNullFatal) { \
  69. \
  70. if (ptr != NULL) { \
  71. AsrpPrintDbgMsg(_asrinfo, "Pointer being allocated not NULL.\r\n"); \
  72. MYASSERT(0); \
  73. } \
  74. \
  75. ptr = MyMalloc(sz); \
  76. \
  77. if (ptr) { \
  78. memset(ptr, 0, sz); \
  79. } \
  80. \
  81. if (!ptr) { \
  82. if ((BOOLEAN) IsNullFatal) { \
  83. AsrpPrintDbgMsg(_asrerror, "Setup was unable to allocate memory.\r\n"); \
  84. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); \
  85. } \
  86. else { \
  87. AsrpPrintDbgMsg(_asrwarn, "Warning. Setup was unable to allocate memory.\r\n"); \
  88. } \
  89. } \
  90. }
  91. //
  92. // _AsrFree
  93. // Macro description:
  94. // Frees ptr and resets it to NULL.
  95. // Asserts if ptr was already NULL
  96. //
  97. #define _AsrFree(ptr) { \
  98. \
  99. if (NULL != ptr) { \
  100. MyFree(ptr); \
  101. ptr = NULL; \
  102. } \
  103. else { \
  104. AsrpPrintDbgMsg(_asrlog, "Attempt to free null Pointer.\r\n"); \
  105. MYASSERT(0); \
  106. } \
  107. }
  108. #define _AsrFreeIfNotNull(ptr) { \
  109. if (NULL != ptr) { \
  110. MyFree(ptr); \
  111. ptr = NULL; \
  112. } \
  113. }
  114. //
  115. // One ASR_RECOVERY_APP_NODE struct is created for each entry
  116. // in the [COMMANDS] section of asr.sif.
  117. //
  118. typedef struct _ASR_RECOVERY_APP_NODE {
  119. struct _ASR_RECOVERY_APP_NODE *Next;
  120. //
  121. // Expect this to always be 1
  122. //
  123. LONG SystemKey;
  124. //
  125. // The sequence number according to which the apps are run. If
  126. // two apps have the same sequence number, the app that appears
  127. // first in the sif file is run.
  128. //
  129. LONG SequenceNumber;
  130. //
  131. // The "actionOnCompletion" field for the app. If CriticalApp is
  132. // non-zero, and the app returns an non-zero exit-code, we shall
  133. // consider it a fatal failure and quit out of ASR.
  134. //
  135. LONG CriticalApp;
  136. //
  137. // The app to be launched
  138. //
  139. PWSTR RecoveryAppCommand;
  140. //
  141. // The paramaters for the app. This is just concatenated to the
  142. // string above. May be NULL.
  143. //
  144. PWSTR RecoveryAppParams;
  145. } ASR_RECOVERY_APP_NODE, *PASR_RECOVERY_APP_NODE;
  146. //
  147. // This contains our list of entries in the COMMANDS section,
  148. // sorted in order of sequence numbers.
  149. //
  150. typedef struct _ASR_RECOVERY_APP_LIST {
  151. PASR_RECOVERY_APP_NODE First; // Head
  152. PASR_RECOVERY_APP_NODE Last; // Tail
  153. LONG AppCount; // NumEntries
  154. } ASR_RECOVERY_APP_LIST, *PASR_RECOVERY_APP_LIST;
  155. //
  156. // We call this to change the boot.ini timeout value to 30 seconds
  157. //
  158. extern BOOL
  159. ChangeBootTimeout(IN UINT Timeout);
  160. //
  161. // From asr.c
  162. //
  163. extern BOOL
  164. AsrpRestoreNonCriticalDisksW(
  165. IN PCWSTR lpSifPath,
  166. IN BOOL bAllOrNothing
  167. );
  168. extern BOOL
  169. AsrpRestoreTimeZoneInformation(
  170. IN PCWSTR lpSifPath
  171. );
  172. //
  173. // Indices for fields in the [COMMANDS] section.
  174. //
  175. typedef enum _SIF_COMMANDS_FIELD_INDEX {
  176. ASR_SIF_COMMANDS_KEY = 0,
  177. ASR_SIF_SYSTEM_KEY, // Expected to always be "1"
  178. ASR_SIF_SEQUENCE_NUMBER,
  179. ASR_SIF_ACTION_ON_COMPLETION,
  180. ASR_SIF_COMMAND_STRING,
  181. ASR_SIF_COMMAND_PARAMETERS, // May be NULL
  182. SIF_SIF_NUMFIELDS // Must always be last
  183. } SIF_COMMANDS_FIELD_INDEX;
  184. #define _Asr_CHECK_BOOLEAN(b,msg) \
  185. if((b) == FALSE) { \
  186. AsrpFatalErrorExit(MSG_FATAL_ERROR, __LINE__, (msg)); \
  187. }
  188. ///////////////////////////////////////////////////////////////////////////////
  189. // Private Functions
  190. ///////////////////////////////////////////////////////////////////////////////
  191. //
  192. // Logs the message to the asr error file. Note that
  193. // AsrpInitialiseErrorFile must have been called once before
  194. // this routine is used.
  195. //
  196. VOID
  197. AsrpLogErrorMessage(
  198. IN PCWSTR buffer
  199. )
  200. {
  201. HANDLE hFile = NULL;
  202. DWORD bytesWritten = 0;
  203. if (Gbl_AsrErrorFilePath) {
  204. //
  205. // Open the error log
  206. //
  207. hFile = CreateFileW(
  208. Gbl_AsrErrorFilePath, // lpFileName
  209. GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess
  210. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  211. NULL, // lpSecurityAttributes
  212. OPEN_ALWAYS, // dwCreationFlags
  213. FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes
  214. NULL // hTemplateFile
  215. );
  216. if ((!hFile) || (INVALID_HANDLE_VALUE == hFile)) {
  217. return;
  218. }
  219. //
  220. // Move to the end of file
  221. //
  222. SetFilePointer(hFile, 0L, NULL, FILE_END);
  223. //
  224. // Add our error string
  225. //
  226. WriteFile(hFile,
  227. buffer,
  228. (wcslen(buffer) * sizeof(WCHAR)),
  229. &bytesWritten,
  230. NULL
  231. );
  232. //
  233. // And we're done
  234. //
  235. CloseHandle(hFile);
  236. }
  237. }
  238. //
  239. // Logs the message to the asr log file. Note that
  240. // AsrpInitialiseLogFile must have been called once before
  241. // this routine is used.
  242. //
  243. VOID
  244. AsrpLogMessage(
  245. IN CONST char Module,
  246. IN CONST ULONG Line,
  247. IN CONST ULONG MesgLevel,
  248. IN CONST PCSTR Message
  249. )
  250. {
  251. SYSTEMTIME currentTime;
  252. DWORD bytesWritten = 0;
  253. char buffer[4196];
  254. GetSystemTime(&currentTime);
  255. sprintf(buffer,
  256. "[%04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu] %c%lu %s%s",
  257. currentTime.wYear,
  258. currentTime.wMonth,
  259. currentTime.wDay,
  260. currentTime.wHour,
  261. currentTime.wMinute,
  262. currentTime.wSecond,
  263. currentTime.wMilliseconds,
  264. Module,
  265. Line,
  266. ((DPFLTR_ERROR_LEVEL == MesgLevel) ? "(Error:ASR) " : (DPFLTR_WARNING_LEVEL == MesgLevel ? "(Warning:ASR) " : "")),
  267. Message
  268. );
  269. if (Gbl_AsrLogFileHandle) {
  270. WriteFile(Gbl_AsrLogFileHandle,
  271. buffer,
  272. (strlen(buffer) * sizeof(char)),
  273. &bytesWritten,
  274. NULL
  275. );
  276. }
  277. }
  278. VOID
  279. AsrpPrintDbgMsg(
  280. IN CONST char Module,
  281. IN CONST ULONG Line,
  282. IN CONST ULONG MesgLevel,
  283. IN PCSTR FormatString,
  284. ...)
  285. /*++
  286. Description:
  287. This prints a debug message AND makes the appropriate entries in
  288. the log and error files.
  289. Arguments:
  290. Line pass in __LINE__
  291. MesgLevel DPFLTR_ levels
  292. FormatString Formatted Message String to be printed.
  293. Returns:
  294. --*/
  295. {
  296. char str[4096]; // the message better fit in this
  297. va_list arglist;
  298. DbgPrintEx(DPFLTR_SETUP_ID, MesgLevel, "ASR %c%lu ", Module, Line);
  299. va_start(arglist, FormatString);
  300. wvsprintfA(str, FormatString, arglist);
  301. va_end(arglist);
  302. DbgPrintEx(DPFLTR_SETUP_ID, MesgLevel, str);
  303. if ((DPFLTR_ERROR_LEVEL == MesgLevel) ||
  304. (DPFLTR_WARNING_LEVEL == MesgLevel) ||
  305. (DPFLTR_TRACE_LEVEL == MesgLevel)
  306. ) {
  307. AsrpLogMessage(Module, Line, MesgLevel, str);
  308. }
  309. }
  310. //
  311. // This will terminate Setup and cause a reboot. This is called
  312. // on Out of Memory errors
  313. //
  314. VOID
  315. AsrpFatalErrorExit(
  316. IN LONG MsgValue,
  317. IN LONG LineNumber,
  318. IN PWSTR MessageString
  319. )
  320. {
  321. AsrpPrintDbgMsg(THIS_MODULE, LineNumber, DPFLTR_ERROR_LEVEL, "Fatal Error: %ws (%lu)",
  322. (MessageString ? MessageString : L"(No error string)"), GetLastError()
  323. );
  324. FatalError(MsgValue, MessageString, 0, 0);
  325. }
  326. //
  327. // This just adds the new node to the end of the list.
  328. // Note that this does NOT sort the list by sequenceNumber:
  329. // we'll do that later on
  330. //
  331. VOID
  332. AsrpAppendNodeToList(
  333. IN PASR_RECOVERY_APP_LIST pList,
  334. IN PASR_RECOVERY_APP_NODE pNode
  335. )
  336. {
  337. //
  338. // Insert at end of list.
  339. //
  340. pNode->Next = NULL;
  341. if (pList->AppCount == 0) {
  342. pList->First = pNode;
  343. } else {
  344. pList->Last->Next = pNode;
  345. }
  346. pList->Last = pNode;
  347. pList->AppCount += 1;
  348. }
  349. //
  350. // Pops off the first node in the list. The list is sorted
  351. // in order of increasing SequenceNumber's at this point.
  352. //
  353. PASR_RECOVERY_APP_NODE
  354. AsrpRemoveFirstNodeFromList(
  355. IN PASR_RECOVERY_APP_LIST pList
  356. )
  357. {
  358. PASR_RECOVERY_APP_NODE pNode;
  359. if(pList->AppCount == 0) {
  360. return NULL;
  361. }
  362. pNode = pList->First;
  363. pList->First = pNode->Next;
  364. pList->AppCount -= 1;
  365. MYASSERT(pList->AppCount >= 0);
  366. return pNode;
  367. }
  368. PWSTR // must be freed by caller
  369. AsrpExpandEnvStrings(
  370. IN CONST PCWSTR OriginalString
  371. )
  372. {
  373. PWSTR expandedString = NULL;
  374. UINT cchSize = MAX_PATH + 1, // start with a reasonable default
  375. cchRequiredSize = 0;
  376. BOOL result = FALSE;
  377. _AsrAlloc(expandedString, (cchSize * sizeof(WCHAR)), TRUE);
  378. cchRequiredSize = ExpandEnvironmentStringsW(OriginalString,
  379. expandedString,
  380. cchSize
  381. );
  382. if (cchRequiredSize > cchSize) {
  383. //
  384. // Buffer wasn't big enough; free and re-allocate as needed
  385. //
  386. _AsrFree(expandedString);
  387. cchSize = cchRequiredSize + 1;
  388. _AsrAlloc(expandedString, (cchSize * sizeof(WCHAR)), TRUE);
  389. cchRequiredSize = ExpandEnvironmentStringsW(OriginalString,
  390. expandedString,
  391. cchSize
  392. );
  393. }
  394. if ((0 == cchRequiredSize) || (cchRequiredSize > cchSize)) {
  395. //
  396. // Either the function failed, or the buffer wasn't big enough
  397. // even on the second try
  398. //
  399. _AsrFree(expandedString); // sets it to NULL
  400. }
  401. return expandedString;
  402. }
  403. //
  404. // Builds the invocation string, as the name suggests. It expands out
  405. // the environment variables that apps are allowed to use in the
  406. // sif file, and adds in /sifpath=<path to the sif file> at the end
  407. // of the command. So for an entry in the COMMANDS section of
  408. // the form:
  409. // 4=1,3500,0,"%TEMP%\app.exe","/param1 /param2"
  410. //
  411. // the invocation string would be of the form:
  412. // c:\windows\temp\app.exe /param1 /param2 /sifpath=c:\windows\repair\asr.sif
  413. //
  414. //
  415. PWSTR
  416. AsrpBuildInvocationString(
  417. IN PASR_RECOVERY_APP_NODE pNode // must not be NULL
  418. )
  419. {
  420. PWSTR app = pNode->RecoveryAppCommand,
  421. args = pNode->RecoveryAppParams,
  422. cmd = NULL,
  423. fullcmd = NULL;
  424. DWORD size = 0;
  425. MYASSERT(app);
  426. //
  427. // Build an command line that looks like...
  428. //
  429. // "%TEMP%\ntbackup recover /1 /sifpath=%systemroot%\repair\asr.sif"
  430. //
  431. // The /sifpath parameter is added to all apps being launched
  432. //
  433. //
  434. // Allocate memory for the cmd line
  435. //
  436. size = sizeof(WCHAR) *
  437. (
  438. wcslen(app) + // app name "%TEMP%\ntbackup"
  439. (args ? wcslen(args) : 0) + // arguments "recover /1"
  440. wcslen(AsrCommandSuffix) + // suffix "/sifpath=%systemroot%\repair\asr.sif"
  441. 4 // spaces and null
  442. );
  443. _AsrAlloc(cmd, size, TRUE); // won't return if alloc fails
  444. //
  445. // Build the string
  446. //
  447. swprintf(cmd,
  448. L"%ws %ws %ws",
  449. app,
  450. (args? args: L""),
  451. AsrCommandSuffix
  452. );
  453. //
  454. // Expand the %% stuff, to build the full path
  455. //
  456. fullcmd = AsrpExpandEnvStrings(cmd);
  457. _AsrFree(cmd);
  458. return fullcmd;
  459. }
  460. BOOL
  461. AsrpRetryIsServiceRunning(
  462. IN PWSTR ServiceName,
  463. IN UINT MaxRetries
  464. )
  465. {
  466. SERVICE_STATUS status;
  467. SC_HANDLE svcHandle = NULL, // handle to the service
  468. scmHandle = NULL; // handle to the service control manager
  469. UINT count = 0;
  470. BOOL errorsEncountered = FALSE;
  471. PWSTR errString = NULL;
  472. scmHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
  473. if (!scmHandle) {
  474. //
  475. // OpenSCManager() call failed - we are broke.
  476. //
  477. AsrpPrintDbgMsg(_asrerror,
  478. "Setup was unable to open the service control manager. The error code returned was 0x%x.\r\n",
  479. GetLastError()
  480. );
  481. errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_OPEN_SCM);
  482. if (errString) {
  483. swprintf(g_szErrorMessage, errString, GetLastError());
  484. AsrpLogErrorMessage(g_szErrorMessage);
  485. MyFree(errString);
  486. errString = NULL;
  487. }
  488. else {
  489. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0);
  490. }
  491. errorsEncountered = TRUE;
  492. goto EXIT;
  493. }
  494. svcHandle = OpenServiceW(scmHandle, ServiceName, SERVICE_QUERY_STATUS);
  495. if (!svcHandle) {
  496. //
  497. // OpenService() call failed - we are broke.
  498. //
  499. AsrpPrintDbgMsg(_asrerror,
  500. "Setup was unable to start the service \"%ws\". The error code returned was 0x%x.\r\n",
  501. ServiceName,
  502. GetLastError()
  503. );
  504. errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_START_SERVICE);
  505. if (errString) {
  506. swprintf(g_szErrorMessage, errString, ServiceName, GetLastError());
  507. AsrpLogErrorMessage(g_szErrorMessage);
  508. MyFree(errString);
  509. errString = NULL;
  510. }
  511. else {
  512. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0);
  513. }
  514. errorsEncountered = TRUE;
  515. goto EXIT;
  516. }
  517. //
  518. // Got the service opened for query. See if it's running, and
  519. // if not, go thru the retry loop.
  520. //
  521. while (count < MaxRetries) {
  522. if (!QueryServiceStatus(svcHandle, &status)) {
  523. //
  524. // Couldn't query the status of the service
  525. //
  526. AsrpPrintDbgMsg(_asrerror,
  527. "Setup was unable to query the status of service \"%ws\". The error code returned was 0x%x\r\n",
  528. ServiceName,
  529. GetLastError()
  530. );
  531. errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_START_SERVICE);
  532. if (errString) {
  533. swprintf(g_szErrorMessage, errString, ServiceName, GetLastError());
  534. AsrpLogErrorMessage(g_szErrorMessage);
  535. MyFree(errString);
  536. errString = NULL;
  537. }
  538. else {
  539. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0);
  540. }
  541. errorsEncountered = TRUE;
  542. goto EXIT;
  543. }
  544. if (status.dwCurrentState == SERVICE_RUNNING) {
  545. //
  546. // Service is running - we can proceed.
  547. //
  548. break;
  549. }
  550. ++count;
  551. AsrpPrintDbgMsg(_asrinfo,
  552. "Attempting to start service [%ws]: status = [%d], retry [%d]\r\n",
  553. ServiceName,
  554. status.dwCurrentState,
  555. count
  556. );
  557. Sleep(2000);
  558. }
  559. EXIT:
  560. if ((svcHandle) && (INVALID_HANDLE_VALUE != svcHandle)) {
  561. CloseServiceHandle(svcHandle);
  562. svcHandle = NULL;
  563. }
  564. if ((scmHandle) && (INVALID_HANDLE_VALUE != svcHandle)) {
  565. CloseServiceHandle(scmHandle);
  566. scmHandle = NULL;
  567. }
  568. if ((errorsEncountered) || (count >= MaxRetries)) {
  569. return FALSE;
  570. }
  571. else {
  572. return TRUE;
  573. }
  574. }
  575. //
  576. // Before launching apps, we need RSM (specifically, the backup app
  577. // might need RSM to access its backup media)
  578. //
  579. VOID
  580. AsrpStartNtmsService(VOID)
  581. {
  582. BOOL result = TRUE;
  583. DWORD exitCode = ERROR_SUCCESS;
  584. PWSTR registerNtmsCommand = NULL;
  585. AsrpPrintDbgMsg(_asrinfo, "Entered InitNtmsService()\r\n");
  586. //
  587. // RSM isn't setup to run during GUI mode setup, but the back-up app is
  588. // likely going to need access to tape-drives and other RSM devices.
  589. // So we regsvr32 the appropriate dll's and start the service
  590. //
  591. // Register the rsmps.dll using:
  592. // regsvr32 /s %Systemroot%\system32\rsmps.dll
  593. //
  594. result = FALSE;
  595. registerNtmsCommand = AsrpExpandEnvStrings(L"regsvr32 /s %systemroot%\\system32\\rsmps.dll");
  596. if (registerNtmsCommand) {
  597. result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode);
  598. AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand);
  599. _AsrFree(registerNtmsCommand);
  600. }
  601. _Asr_CHECK_BOOLEAN(result, L"regsvr32 /s %systemroot%\\rsmps.dll failed\r\n");
  602. //
  603. // Register the rsmmllsv.exe using:
  604. // %SystemRoot%\system32\rsmmllsv.exe /regserver
  605. //
  606. result = FALSE;
  607. registerNtmsCommand = AsrpExpandEnvStrings(L"%systemroot%\\system32\\rsmmllsv.exe /regserver");
  608. if (registerNtmsCommand) {
  609. result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode);
  610. AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand);
  611. _AsrFree(registerNtmsCommand);
  612. }
  613. _Asr_CHECK_BOOLEAN(result, L"%systemroot%\\system32\\rsmmllsv.exe /regserver failed\r\n");
  614. //
  615. // Register the ntmssvc.dll using:
  616. // regsvr32 /s %SystemRoot%\system32\ntmssvc.dll
  617. //
  618. result = FALSE;
  619. registerNtmsCommand = AsrpExpandEnvStrings(L"regsvr32 /s %systemroot%\\system32\\ntmssvc.dll");
  620. if (registerNtmsCommand) {
  621. result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode);
  622. AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand);
  623. _AsrFree(registerNtmsCommand);
  624. }
  625. _Asr_CHECK_BOOLEAN(result, L"regsvr32 /s %systemroot%\\ntmssvc.dll failed\r\n");
  626. //
  627. // Register the rsmsink.exe using:
  628. // %SystemRoot%\system32\rsmsink.exe /regserver
  629. //
  630. result = FALSE;
  631. registerNtmsCommand = AsrpExpandEnvStrings(L"%systemroot%\\system32\\rsmsink.exe /regserver");
  632. if (registerNtmsCommand) {
  633. result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode);
  634. AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand);
  635. _AsrFree(registerNtmsCommand);
  636. }
  637. _Asr_CHECK_BOOLEAN(result, L"%systemroot%\\system32\\rsmsink.exe /regserver failed\r\n");
  638. //
  639. // Now, start the ntms service.
  640. //
  641. result = SetupStartService(L"ntmssvc", FALSE);
  642. _Asr_CHECK_BOOLEAN(result, L"Could not start RSM service (ntmssvc).\r\n");
  643. //
  644. // Check for ntms running, give a few retries.
  645. //
  646. result = AsrpRetryIsServiceRunning(L"ntmssvc", 30);
  647. _Asr_CHECK_BOOLEAN(result, L"Failed to start RSM service after 30 retries.\r\n");
  648. AsrpPrintDbgMsg(_asrinfo, "RSM service (ntmssvc) started.\r\n");
  649. }
  650. PWSTR
  651. AsrpReadField(
  652. PINFCONTEXT pInfContext,
  653. DWORD FieldIndex,
  654. BOOL NullOkay
  655. )
  656. {
  657. PWSTR data = NULL;
  658. UINT reqdSize = 0;
  659. BOOL result = FALSE;
  660. //
  661. // Allocate memory and read the data
  662. //
  663. _AsrAlloc(data, (sizeof(WCHAR) * (MAX_PATH + 1)), TRUE);
  664. result = SetupGetStringFieldW(
  665. pInfContext,
  666. FieldIndex,
  667. data,
  668. MAX_PATH + 1,
  669. &reqdSize
  670. );
  671. if (!result) {
  672. DWORD status = GetLastError();
  673. //
  674. // If our buffer was too small, allocate a larger buffer
  675. // and try again
  676. //
  677. if (ERROR_INSUFFICIENT_BUFFER == status) {
  678. status = ERROR_SUCCESS;
  679. _AsrFree(data);
  680. _AsrAlloc(data, (sizeof(WCHAR) * reqdSize), TRUE);
  681. result = SetupGetStringFieldW(
  682. pInfContext,
  683. FieldIndex,
  684. data,
  685. reqdSize,
  686. NULL // don't need required size any more
  687. );
  688. }
  689. }
  690. if (!result) {
  691. _AsrFree(data);
  692. _Asr_CHECK_BOOLEAN(NullOkay, L"Could not read entry from commands section");
  693. // Never returns if NullOkay is FALSE.
  694. // Memory leaks here then, since we don't free some structs. But
  695. // it's a fatal error, so the system must be rebooted anyway
  696. //
  697. }
  698. return data;
  699. }
  700. //
  701. // This adds in the "Instance" value under the ASR key.
  702. // Third party applications (or Windows components like DTC) can use
  703. // this to determine if a new ASR has been run since the last time we
  704. // booted, and can take any actions they need to. For instance, the
  705. // DTC log file needs to be recreated after an ASR, since it is not
  706. // backed-up or restored by the backup app, and Dtc refuses to start
  707. // if it doesn't find a log file when it expects one.
  708. //
  709. VOID
  710. AsrpAddRegistryEntry()
  711. {
  712. LONG result = 0;
  713. HKEY regKey = NULL;
  714. WCHAR szLastInstanceData[40];
  715. DWORD cbLastInstanceData = 0;
  716. SYSTEMTIME currentTime;
  717. GUID asrInstanceGuid;
  718. PWSTR lpGuidString = NULL;
  719. RPC_STATUS rpcStatus = RPC_S_OK;
  720. //
  721. // We try to set the key to a newly generated GUID, to make sure it is
  722. // unique (and different from the previous value stored there). If, for
  723. // some reason, we aren't able to generate a GUID, we'll just store the
  724. // current date and time as a string--that should be unique, too.
  725. //
  726. rpcStatus = UuidCreate(
  727. &asrInstanceGuid
  728. );
  729. if (RPC_S_OK == rpcStatus) {
  730. //
  731. // Convert the GUID to a printable string
  732. //
  733. rpcStatus = UuidToStringW(
  734. &asrInstanceGuid,
  735. &lpGuidString
  736. );
  737. if (RPC_S_OK == rpcStatus) {
  738. wsprintf(szLastInstanceData,
  739. L"%ws",
  740. lpGuidString
  741. );
  742. cbLastInstanceData = wcslen(szLastInstanceData)*sizeof(WCHAR);
  743. }
  744. if (lpGuidString) {
  745. RpcStringFreeW(&lpGuidString);
  746. }
  747. }
  748. if (RPC_S_OK != rpcStatus) {
  749. //
  750. // We couldn't get a GUID. Let's store the time-stamp ...
  751. //
  752. GetSystemTime(&currentTime);
  753. wsprintf(szLastInstanceData,
  754. L"%04hu%02hu%02hu%02hu%02hu%02hu%03hu",
  755. currentTime.wYear,
  756. currentTime.wMonth,
  757. currentTime.wDay,
  758. currentTime.wHour,
  759. currentTime.wMinute,
  760. currentTime.wSecond,
  761. currentTime.wMilliseconds
  762. );
  763. cbLastInstanceData = wcslen(szLastInstanceData)*sizeof(WCHAR);
  764. }
  765. result = RegCreateKeyExW(
  766. HKEY_LOCAL_MACHINE, // hKey
  767. Asr_ControlAsrRegKey, // lpSubKey
  768. 0, // reserved
  769. NULL, // lpClass
  770. REG_OPTION_NON_VOLATILE, // dwOptions
  771. MAXIMUM_ALLOWED, // samDesired
  772. NULL, // lpSecurityAttributes
  773. &regKey, // phkResult
  774. NULL // lpdwDisposition
  775. );
  776. if ((ERROR_SUCCESS != result) || (!regKey)) {
  777. AsrpPrintDbgMsg(_asrwarn,
  778. "Could not create the Control\\ASR registry entry (0x%x).\r\n",
  779. result
  780. );
  781. return;
  782. }
  783. result = RegSetValueExW(
  784. regKey, // hKey
  785. Asr_LastInstanceRegValue, // lpValueName
  786. 0L, // reserved
  787. REG_SZ, // dwType
  788. (LPBYTE)szLastInstanceData, // lpData
  789. cbLastInstanceData // cbData
  790. );
  791. RegCloseKey(regKey);
  792. if (ERROR_SUCCESS != result) {
  793. AsrpPrintDbgMsg(_asrwarn,
  794. "Could not set the ASR instance-ID in the registry (0x%x).\r\n",
  795. result
  796. );
  797. return;
  798. }
  799. AsrpPrintDbgMsg(_asrlog,
  800. "Set the ASR instance-ID at [%ws\\%ws] value to [%ws]\r\n",
  801. Asr_ControlAsrRegKey,
  802. Asr_LastInstanceRegValue,
  803. szLastInstanceData
  804. );
  805. }
  806. VOID
  807. AsrpSetEnvironmentVariables()
  808. {
  809. PWSTR TempPath = AsrpExpandEnvStrings(AsrTempDir);
  810. if (NULL == TempPath) {
  811. return;
  812. }
  813. if (!CreateDirectoryW(TempPath, NULL)) {
  814. AsrpPrintDbgMsg(_asrwarn,
  815. "Unable to create TEMP directory [%ws] (%lu)\r\n",
  816. TempPath, GetLastError()
  817. );
  818. }
  819. AsrpPrintDbgMsg(_asrlog,
  820. "Setting environment variables TEMP and TMP to [%ws]\r\n",
  821. TempPath
  822. );
  823. if (!SetEnvironmentVariableW(L"TEMP", TempPath)) {
  824. AsrpPrintDbgMsg(_asrwarn,
  825. "Unable to set environment variable TEMP to [%ws] (%lu)\r\n",
  826. TempPath, GetLastError()
  827. );
  828. }
  829. if (!SetEnvironmentVariableW(L"TMP", TempPath)) {
  830. AsrpPrintDbgMsg(_asrwarn,
  831. "Unable to set environment variable TEMP to [%ws] (%lu)\r\n",
  832. TempPath, GetLastError()
  833. );
  834. }
  835. _AsrFree(TempPath);
  836. return;
  837. }
  838. VOID
  839. AsrpInitExecutionEnv(
  840. OUT PASR_RECOVERY_APP_LIST List
  841. )
  842. {
  843. PWSTR stateFileName = NULL;
  844. HINF sifHandle = NULL;
  845. LONG lineCount = 0,
  846. line = 0;
  847. BOOL result = FALSE;
  848. INFCONTEXT infContext;
  849. //
  850. // Start the RSM service
  851. //
  852. AsrpStartNtmsService();
  853. //
  854. // Open the asr.sif file and build the list
  855. // of commands to be launched.
  856. //
  857. stateFileName = AsrpExpandEnvStrings(AsrSifPath);
  858. if (!stateFileName) {
  859. AsrpPrintDbgMsg(_asrerror, "Setup was unable to locate the ASR state file asr.sif on this machine.\r\n");
  860. FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0);
  861. }
  862. sifHandle = SetupOpenInfFileW(
  863. stateFileName,
  864. NULL, // Inf Class
  865. INF_STYLE_WIN4,
  866. NULL // Error-line
  867. );
  868. if ((!sifHandle) || (INVALID_HANDLE_VALUE == sifHandle)) {
  869. AsrpPrintDbgMsg(_asrerror,
  870. "Setup was unable to process the ASR state file %ws (0x%x). This could indicate that the file is corrupt, or has been modified since the last ASR backup.\r\n",
  871. stateFileName,
  872. GetLastError());
  873. _AsrFree(stateFileName);
  874. FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0);
  875. }
  876. _AsrFree(stateFileName);
  877. //
  878. // Read the COMMANDS section, and add each command to our list
  879. //
  880. lineCount = SetupGetLineCountW(sifHandle, AsrCommandsSectionName);
  881. for (line = 0; line < lineCount; line++) {
  882. //
  883. // Create a new node
  884. //
  885. PASR_RECOVERY_APP_NODE pNode = NULL;
  886. _AsrAlloc(pNode, (sizeof(ASR_RECOVERY_APP_NODE)), TRUE);
  887. //
  888. // Get the inf context for the line in asr.sif. This will be used
  889. // to read the fields on that line
  890. //
  891. result = SetupGetLineByIndexW(
  892. sifHandle,
  893. AsrCommandsSectionName,
  894. line,
  895. &infContext
  896. );
  897. _Asr_CHECK_BOOLEAN(result, L"SetupGetLinebyIndex failed");
  898. //
  899. // Read in the int fields
  900. //
  901. result = SetupGetIntField(
  902. &infContext,
  903. ASR_SIF_SYSTEM_KEY,
  904. &(pNode->SystemKey)
  905. );
  906. _Asr_CHECK_BOOLEAN(result, L"could not get system key in commands section");
  907. result = SetupGetIntField(
  908. &infContext,
  909. ASR_SIF_SEQUENCE_NUMBER,
  910. &(pNode->SequenceNumber)
  911. );
  912. _Asr_CHECK_BOOLEAN(result, L"could not get sequence number in commands section");
  913. result = SetupGetIntField(
  914. &infContext,
  915. ASR_SIF_ACTION_ON_COMPLETION,
  916. &(pNode->CriticalApp)
  917. );
  918. _Asr_CHECK_BOOLEAN(result, L"could not get criticalApp in commands section");
  919. //
  920. // Read in the string fields
  921. //
  922. pNode->RecoveryAppCommand = AsrpReadField(
  923. &infContext,
  924. ASR_SIF_COMMAND_STRING,
  925. FALSE // Null not okay
  926. );
  927. pNode->RecoveryAppParams = AsrpReadField(
  928. &infContext,
  929. ASR_SIF_COMMAND_PARAMETERS,
  930. TRUE // Null okay
  931. );
  932. //
  933. // Add this node to our list, and move on to next
  934. //
  935. AsrpAppendNodeToList(List, pNode);
  936. }
  937. SetupCloseInfFile(sifHandle);
  938. }
  939. //
  940. // Bubble sort ...
  941. //
  942. VOID
  943. AsrpSortAppListBySequenceNumber(PASR_RECOVERY_APP_LIST pList)
  944. {
  945. PASR_RECOVERY_APP_NODE
  946. pCurr = NULL,
  947. pNext = NULL,
  948. *ppPrev = NULL;
  949. BOOLEAN done = FALSE;
  950. if ((!pList) || (!pList->First)) {
  951. MYASSERT(0 && L"Recovery App List pList is NULL");
  952. return;
  953. }
  954. //
  955. // Start the outer loop. Each iteration of the outer loop includes a
  956. // full pass down the list, and runs until the inner loop is satisfied
  957. // that no more passes are needed.
  958. //
  959. while (!done) {
  960. //
  961. // Start at the beginning of the list for each inner (node) loop.
  962. //
  963. // We will initialize a pointer *to the pointer* which points to
  964. // the current node - this pointer might be the address of the "list
  965. // first" pointer (as it always will be at the start of an inner loop),
  966. // or as the inner loop progresses, it might be the address of the
  967. // "next" pointer in the previous node. In either case, the pointer
  968. // to which ppPrev points will be changed in the event of a node swap.
  969. //
  970. pCurr = pList->First;
  971. ppPrev = &(pList->First);
  972. done = TRUE;
  973. MYASSERT(pCurr);
  974. while (TRUE) {
  975. pNext = pCurr->Next;
  976. //
  977. // If the current node is the last one, reset to the beginning
  978. // and break out to start a new inner loop.
  979. //
  980. if (pNext == NULL) {
  981. pCurr = pList->First;
  982. break;
  983. }
  984. //
  985. // If the node *after* the current node has a lower sequence
  986. // number, fix up the pointers to swap the two nodes.
  987. //
  988. if (pCurr->SequenceNumber > pNext->SequenceNumber) {
  989. done = FALSE;
  990. pCurr->Next = pNext->Next;
  991. pNext->Next = pCurr;
  992. *ppPrev = pNext;
  993. ppPrev = &(pNext->Next);
  994. }
  995. else {
  996. ppPrev = &(pCurr->Next);
  997. pCurr = pCurr->Next;
  998. }
  999. }
  1000. }
  1001. }
  1002. VOID
  1003. AsrpPerformSifIntegrityCheck(IN HINF Handle)
  1004. {
  1005. //
  1006. // No check for now.
  1007. //
  1008. return;
  1009. }
  1010. //
  1011. // This checks if the following entries are different in setup.log
  1012. // from their values. This could happen because we might have installed
  1013. // to a new disk that has a different disk number
  1014. //
  1015. // [Paths]
  1016. // TargetDevice = "\Device\Harddisk0\Partition2"
  1017. // SystemPartition = "\Device\Harddisk0\Partition1"
  1018. //
  1019. // If they are different, we'll update them.
  1020. //
  1021. BOOL
  1022. AsrpCheckSetupLogDeviceEntries(
  1023. PWSTR CurrentSystemDevice, // used for SystemPartition
  1024. PWSTR CurrentBootDevice, // used for TargetDevice
  1025. PWSTR LogFileName // path to setup.log
  1026. )
  1027. {
  1028. WCHAR szLine[MAX_INF_STRING_LENGTH + 1];
  1029. PWSTR lpLine = NULL;
  1030. BOOL isDifferent = FALSE;
  1031. FILE *fp = NULL;
  1032. INT iNumEntries = 0;
  1033. //
  1034. // Open existing setup.log
  1035. //
  1036. fp = _wfopen(LogFileName, L"r");
  1037. if (!fp) {
  1038. AsrpPrintDbgMsg(_asrwarn,
  1039. "Could not open setup log file [%ws]\r\n",
  1040. LogFileName
  1041. );
  1042. return FALSE;
  1043. }
  1044. //
  1045. // Check each line of the file for the System or Boot device entries
  1046. //
  1047. lpLine = fgetws(szLine, MAX_PATH-1, fp);
  1048. while ((lpLine) && (iNumEntries < 2)) {
  1049. BOOL systemEntry = FALSE;
  1050. BOOL bootEntry = FALSE;
  1051. if (wcsstr(szLine, L"SystemPartition =")) {
  1052. systemEntry = TRUE;
  1053. iNumEntries++;
  1054. }
  1055. if (wcsstr(szLine, L"TargetDevice =")) {
  1056. bootEntry = TRUE;
  1057. iNumEntries++;
  1058. }
  1059. if (systemEntry || bootEntry) {
  1060. PWSTR DeviceName = NULL;
  1061. //
  1062. // Both the system and boot entries must have the full
  1063. // devicepath in them, of the form \Device\Harddisk0\Partition1
  1064. //
  1065. DeviceName = wcsstr(szLine, L"\\Device");
  1066. if (!DeviceName) {
  1067. isDifferent = TRUE;
  1068. AsrpPrintDbgMsg(_asrlog,
  1069. "Marking setup logs different: \\Device\\ not found in boot or system entry\r\n"
  1070. );
  1071. break;
  1072. }
  1073. else {
  1074. //
  1075. // Find the start of the "Hardisk0\Partition1" text after \Device
  1076. //
  1077. PWSTR ss = wcsstr(DeviceName, L"\"");
  1078. if (!ss) {
  1079. isDifferent = TRUE;
  1080. AsrpPrintDbgMsg(_asrlog,
  1081. "Marking setup logs different: \\Device\\ not found in boot or system entry\r\n"
  1082. );
  1083. break;
  1084. }
  1085. else {
  1086. ss[0] = L'\0';
  1087. }
  1088. }
  1089. //
  1090. // And check if this device matches
  1091. //
  1092. if (systemEntry) {
  1093. AsrpPrintDbgMsg(_asrinfo,
  1094. "Comparing System Device. Current:[%ws] setup.log:[%ws]\r\n",
  1095. CurrentSystemDevice,
  1096. DeviceName
  1097. );
  1098. if (wcscmp(DeviceName, CurrentSystemDevice) != 0) {
  1099. isDifferent = TRUE;
  1100. AsrpPrintDbgMsg(_asrlog,
  1101. "System Device has changed. Current:[%ws] setup.log:[%ws]\r\n",
  1102. CurrentSystemDevice,
  1103. DeviceName
  1104. );
  1105. break;
  1106. }
  1107. }
  1108. else if (bootEntry) {
  1109. AsrpPrintDbgMsg(_asrinfo,
  1110. "Comparing Boot Device. Current:[%ws] setup.log:[%ws]\r\n",
  1111. CurrentBootDevice,
  1112. DeviceName
  1113. );
  1114. if (wcscmp(DeviceName, CurrentBootDevice) != 0) {
  1115. isDifferent = TRUE;
  1116. AsrpPrintDbgMsg(_asrlog,
  1117. "Boot device has changed. Current:[%ws] setup.log:[%ws]\r\n",
  1118. CurrentBootDevice,
  1119. DeviceName
  1120. );
  1121. break;
  1122. }
  1123. }
  1124. }
  1125. lpLine = fgetws(szLine, MAX_PATH-1, fp);
  1126. }
  1127. if (!isDifferent) {
  1128. AsrpPrintDbgMsg(_asrinfo, "No changes in system and boot devices for setup.log\r\n");
  1129. }
  1130. fclose(fp);
  1131. fp = NULL;
  1132. return isDifferent;
  1133. }
  1134. //
  1135. // If the setup.log restored by the backup from tape has a different
  1136. // boot or system device marked (we might have picked a new disk in
  1137. // textmode Setup), this will update the relevant entries to match the
  1138. // current boot and system devices.
  1139. //
  1140. VOID
  1141. AsrpMergeSetupLog(
  1142. PWSTR CurrentSystemDevice,
  1143. PWSTR CurrentBootDevice,
  1144. PWSTR LogFileName
  1145. )
  1146. {
  1147. WCHAR szLine[MAX_INF_STRING_LENGTH + 1];
  1148. PWSTR lpLine = NULL,
  1149. lpOldFileName = NULL,
  1150. lpNewFileName = NULL;
  1151. BOOL result = FALSE;
  1152. FILE *fpNew = NULL,
  1153. *fpCurrent = NULL;
  1154. INT iNumEntries = 0;
  1155. //
  1156. // Create the "new" and "old" file names, i.e., "setup.log.new" and "setup.log.old"
  1157. //
  1158. _AsrAlloc(lpNewFileName, ((wcslen(LogFileName) + 5) * sizeof(WCHAR)), TRUE)
  1159. wcscpy(lpNewFileName, LogFileName);
  1160. wcscat(lpNewFileName, L".new");
  1161. _AsrAlloc(lpOldFileName, ((wcslen(LogFileName) + 5) * sizeof(WCHAR)), TRUE);
  1162. wcscpy(lpOldFileName, LogFileName);
  1163. wcscat(lpOldFileName, L".old");
  1164. //
  1165. // Open the current setup.log file.
  1166. //
  1167. fpCurrent = _wfopen(LogFileName, L"r");
  1168. if (!fpCurrent) {
  1169. AsrpPrintDbgMsg(_asrwarn, "Setup was unable to open the setup log file \"%ws\"\r\n", LogFileName);
  1170. goto EXIT;
  1171. }
  1172. //
  1173. // Open the new file - we will write into this one.
  1174. //
  1175. fpNew = _wfopen(lpNewFileName, L"w");
  1176. if (!fpNew) {
  1177. AsrpPrintDbgMsg(_asrwarn, "Setup was unable to open the setup log file \"%ws\"\r\n", lpNewFileName);
  1178. goto EXIT;
  1179. }
  1180. //
  1181. // Read each line in the log file, copy into the new file, unless we hit
  1182. // one of the two lines in question. Once we've seen both of them, don't
  1183. // check for them anymore.
  1184. //
  1185. lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent);
  1186. while (lpLine) {
  1187. BOOL systemEntry = FALSE;
  1188. BOOL bootEntry = FALSE;
  1189. //
  1190. // If we've already found both entries of interest, just copy
  1191. // and continue.
  1192. //
  1193. if (iNumEntries >= 2) {
  1194. fputws(szLine, fpNew);
  1195. lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent);
  1196. continue;
  1197. }
  1198. //
  1199. // Is this line either the boot or system device?
  1200. //
  1201. if (wcsstr(szLine, L"SystemPartition =")) {
  1202. AsrpPrintDbgMsg(_asrlog,
  1203. "Changing SystemPartition in setup.log to %ws\r\n",
  1204. CurrentSystemDevice
  1205. );
  1206. ++iNumEntries;
  1207. wcscpy(szLine, L"SystemPartition = \"");
  1208. wcscat(szLine, CurrentSystemDevice);
  1209. wcscat(szLine, L"\"\n");
  1210. }
  1211. else if (wcsstr(szLine, L"TargetDevice =")) {
  1212. AsrpPrintDbgMsg(_asrlog,
  1213. "Changing TargetDevice in setup.log to %ws\r\n",
  1214. CurrentBootDevice
  1215. );
  1216. ++iNumEntries;
  1217. wcscpy(szLine, L"TargetDevice = \"");
  1218. wcscat(szLine, CurrentBootDevice);
  1219. wcscat(szLine, L"\"\n");
  1220. }
  1221. fputws(szLine, fpNew);
  1222. lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent);
  1223. }
  1224. //
  1225. // Rename the current setup.log to setup.log.old, and setup.log.new to
  1226. // setup.log. Need to delay this until reboot since setup.log is in
  1227. // use.
  1228. //
  1229. result = MoveFileExW(LogFileName,
  1230. lpOldFileName,
  1231. MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT
  1232. );
  1233. if (!result) {
  1234. AsrpPrintDbgMsg(_asrwarn,
  1235. "MoveFileEx([%ws] to [%ws]) failed (%lu)",
  1236. LogFileName, lpOldFileName, GetLastError()
  1237. );
  1238. }
  1239. else {
  1240. result = MoveFileExW(lpNewFileName,
  1241. LogFileName,
  1242. MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT
  1243. );
  1244. if (!result) {
  1245. AsrpPrintDbgMsg(_asrwarn,
  1246. "MoveFileEx([%ws] to [%ws]) failed (%lu)",
  1247. lpNewFileName, LogFileName, GetLastError()
  1248. );
  1249. }
  1250. }
  1251. EXIT:
  1252. if (fpCurrent) {
  1253. fclose(fpCurrent);
  1254. fpCurrent = NULL;
  1255. }
  1256. if (fpNew) {
  1257. fclose(fpNew);
  1258. fpNew = NULL;
  1259. }
  1260. _AsrFree(lpNewFileName);
  1261. _AsrFree(lpOldFileName);
  1262. }
  1263. VOID
  1264. AsrpMergeSetupLogIfNeeded()
  1265. {
  1266. PWSTR currentSystemDevice = NULL,
  1267. currentBootDevice = NULL,
  1268. winntRootDir = NULL,
  1269. setupLogFileName = NULL;
  1270. BOOL isSetupLogDifferent = FALSE;
  1271. //
  1272. // Get the environment variable for the partition devices
  1273. //
  1274. currentSystemDevice = AsrpExpandEnvStrings(Asr_SystemDeviceEnvName);
  1275. currentBootDevice = AsrpExpandEnvStrings(Asr_WinntDeviceEnvName);
  1276. setupLogFileName = AsrpExpandEnvStrings(Asr_SetupLogFilePath);
  1277. if ((!currentSystemDevice) ||
  1278. (!currentBootDevice) ||
  1279. (!setupLogFileName)) {
  1280. goto EXIT;
  1281. }
  1282. //
  1283. // Check if the system and/or boot devices listed in setup.log are
  1284. // different than the current devices
  1285. //
  1286. isSetupLogDifferent = AsrpCheckSetupLogDeviceEntries(
  1287. currentSystemDevice,
  1288. currentBootDevice,
  1289. setupLogFileName
  1290. );
  1291. if (isSetupLogDifferent) {
  1292. //
  1293. // They are different: fix it.
  1294. //
  1295. AsrpMergeSetupLog(currentSystemDevice,
  1296. currentBootDevice,
  1297. setupLogFileName
  1298. );
  1299. }
  1300. EXIT:
  1301. _AsrFreeIfNotNull(setupLogFileName);
  1302. _AsrFreeIfNotNull(currentBootDevice);
  1303. _AsrFreeIfNotNull(currentSystemDevice);
  1304. }
  1305. //
  1306. // This creates an ASR log file at %systemroot%\asr.log,
  1307. // and also initialises Gbl_AsrLogFileHandle.
  1308. //
  1309. VOID
  1310. AsrpInitialiseLogFile()
  1311. {
  1312. PWSTR currentSystemDevice = NULL;
  1313. Gbl_AsrLogFileHandle = NULL;
  1314. Gbl_AsrSystemVolumeHandle = NULL;
  1315. //
  1316. // Get full path to the error file.
  1317. //
  1318. Gbl_AsrLogFilePath = AsrpExpandEnvStrings(Asr_AsrLogFilePath);
  1319. if (!Gbl_AsrLogFilePath) {
  1320. goto OPENSYSTEMHANDLE;
  1321. }
  1322. //
  1323. // Create an empty file (over-write it if it already exists).
  1324. //
  1325. Gbl_AsrLogFileHandle = CreateFileW(
  1326. Gbl_AsrLogFilePath, // lpFileName
  1327. GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess
  1328. FILE_SHARE_READ, // dwShareMode: nobody else should write to the log file while we are
  1329. NULL, // lpSecurityAttributes
  1330. OPEN_ALWAYS, // dwCreationFlags
  1331. FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes: write through so we flush
  1332. NULL // hTemplateFile
  1333. );
  1334. if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) {
  1335. //
  1336. // Move to the end of file
  1337. //
  1338. SetFilePointer(Gbl_AsrLogFileHandle, 0L, NULL, FILE_END);
  1339. }
  1340. else {
  1341. AsrpPrintDbgMsg(_asrlog,
  1342. "Unable to create/open ASR log file at %ws (0x%x)\r\n",
  1343. Gbl_AsrLogFilePath,
  1344. GetLastError()
  1345. );
  1346. }
  1347. OPENSYSTEMHANDLE:
  1348. //
  1349. // Open a handle to the system volume. This is needed since the system
  1350. // disk might otherwise be removed and added back by PnP during the
  1351. // device detecion and re-installation phase (which will cause the
  1352. // HKLM\System\Setup\SystemPartition key to be out-of-sync, and apps/
  1353. // components such as LDM that depend on that key to find the system
  1354. // partition will fail).
  1355. //
  1356. // The more permanent work-around to this involves a change in mountmgr,
  1357. // (such that it updates this key everytime the system volume disappears
  1358. // and reappears) but for now, holding an open handle to the system
  1359. // volume should suffice.
  1360. //
  1361. // See Windows Bugs 155675 for more information.
  1362. //
  1363. currentSystemDevice = AsrpExpandEnvStrings(Asr_SystemDeviceWin32Path);
  1364. if (currentSystemDevice) {
  1365. Gbl_AsrSystemVolumeHandle = CreateFileW(
  1366. currentSystemDevice, // lpFileName
  1367. FILE_READ_ATTRIBUTES, // dwDesiredAccess
  1368. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1369. NULL, // lpSecurityAttributes
  1370. OPEN_EXISTING, // dwCreationFlags
  1371. FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes: write through so we flush
  1372. NULL // hTemplateFile
  1373. );
  1374. if ((Gbl_AsrSystemVolumeHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrSystemVolumeHandle)) {
  1375. AsrpPrintDbgMsg(_asrinfo, "Opened a handle to the system volume %ws\r\n", currentSystemDevice);
  1376. }
  1377. else {
  1378. AsrpPrintDbgMsg(_asrinfo, "Unable to open a handle to the system volume %ws (0x%x)\r\n",
  1379. currentSystemDevice,
  1380. GetLastError()
  1381. );
  1382. }
  1383. _AsrFree(currentSystemDevice);
  1384. }
  1385. else {
  1386. AsrpPrintDbgMsg(_asrinfo, "Unable to get current system volume (0x%x)\r\n", GetLastError());
  1387. }
  1388. }
  1389. //
  1390. // This creates an empty ASR error file at %systemroot%\asr.err,
  1391. // and also initialises Gbl_AsrErrorFilePath with the full path
  1392. // to asr.err
  1393. //
  1394. VOID
  1395. AsrpInitialiseErrorFile()
  1396. {
  1397. HANDLE errorFileHandle = NULL;
  1398. PWSTR lpOldFileName = NULL;
  1399. DWORD size = 0;
  1400. BOOL bResult = FALSE;
  1401. char UnicodeFlag[3];
  1402. //
  1403. // Get full path to the error file.
  1404. //
  1405. Gbl_AsrErrorFilePath = AsrpExpandEnvStrings(Asr_AsrErrorFilePath);
  1406. if (!Gbl_AsrErrorFilePath) {
  1407. return;
  1408. }
  1409. lpOldFileName = AsrpExpandEnvStrings(Asr_OldAsrErrorFilePath);
  1410. if (lpOldFileName) {
  1411. //
  1412. // If the file already exists, move it to asr.err.old
  1413. //
  1414. MoveFileExW(Gbl_AsrErrorFilePath, lpOldFileName, MOVEFILE_REPLACE_EXISTING);
  1415. }
  1416. //
  1417. // Create an empty file (append to it if it already exists), and close it
  1418. // immediately
  1419. //
  1420. errorFileHandle = CreateFileW(
  1421. Gbl_AsrErrorFilePath, // lpFileName
  1422. GENERIC_WRITE, // dwDesiredAccess
  1423. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1424. NULL, // lpSecurityAttributes
  1425. CREATE_ALWAYS, // dwCreationFlags
  1426. FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes
  1427. NULL // hTemplateFile
  1428. );
  1429. if ((errorFileHandle) && (INVALID_HANDLE_VALUE != errorFileHandle)) {
  1430. sprintf(UnicodeFlag, "%c%c", 0xFF, 0xFE);
  1431. WriteFile(errorFileHandle, UnicodeFlag, strlen(UnicodeFlag)*sizeof(char), &size, NULL);
  1432. CloseHandle(errorFileHandle);
  1433. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_TRACE_LEVEL,
  1434. "ASR %c%lu Create ASR error file at %ws\r\n",
  1435. THIS_MODULE, __LINE__, Gbl_AsrErrorFilePath);
  1436. }
  1437. else {
  1438. DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
  1439. "ASR %c%lu (ERROR) Unable to create ASR error file at %ws (0x%lu)\r\n",
  1440. THIS_MODULE, __LINE__, Gbl_AsrErrorFilePath, GetLastError());
  1441. }
  1442. }
  1443. VOID
  1444. AsrpCloseLogFiles() {
  1445. if (Gbl_AsrErrorFilePath) {
  1446. _AsrFree(Gbl_AsrErrorFilePath);
  1447. }
  1448. if (Gbl_AsrLogFilePath) {
  1449. _AsrFree(Gbl_AsrLogFilePath);
  1450. }
  1451. if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) {
  1452. CloseHandle(Gbl_AsrLogFileHandle);
  1453. Gbl_AsrLogFileHandle = NULL;
  1454. }
  1455. }
  1456. //
  1457. // This executes "notepad <Asr-Log-File>". If we encounter a critical
  1458. // failure, we display the error log to the user, and reboot. We
  1459. // document that any critical application that returns a fatal error
  1460. // code is required to make an entry explaining the error in the
  1461. // ASR error file.
  1462. //
  1463. VOID
  1464. AsrpExecuteOnFatalError()
  1465. {
  1466. BOOL result = FALSE;
  1467. DWORD exitCode = 0;
  1468. PWSTR onFatalCmd = NULL;
  1469. if (!Gbl_AsrErrorFilePath) {
  1470. MYASSERT(0 && L"ExecuteOnFatalError called before InitialiseErrorFile: Gbl_ErrorFilePath is NULL");
  1471. return;
  1472. }
  1473. //
  1474. // Make the error file read-only, so that the user's changes
  1475. // aren't accidentally saved.
  1476. //
  1477. result = SetFileAttributesW(Gbl_AsrErrorFilePath, FILE_ATTRIBUTE_READONLY);
  1478. if (!result) {
  1479. AsrpPrintDbgMsg(_asrwarn,
  1480. "Setup was unable to reset file attributes on file [%ws] to read-only (0x%x)\r\n",
  1481. Gbl_AsrErrorFilePath,
  1482. GetLastError()
  1483. );
  1484. }
  1485. //
  1486. // Pop up the ASR failed wizard page.
  1487. //
  1488. //
  1489. // Finally run "notepad <asr-log-file>"
  1490. //
  1491. onFatalCmd = AsrpExpandEnvStrings(Asr_FatalErrorCommand);
  1492. if (!onFatalCmd) {
  1493. //
  1494. // Nothing we can do here--we couldn't find the command
  1495. // to execute on fatal errors. Just bail--this is going
  1496. // to make the system reboot.
  1497. //
  1498. return;
  1499. }
  1500. result = InvokeExternalApplication(
  1501. NULL, // no Application Name
  1502. onFatalCmd, // the full command string
  1503. &exitCode // we want a synchronous execution
  1504. );
  1505. if (!result) {
  1506. SetFileAttributesW(Gbl_AsrErrorFilePath, FILE_ATTRIBUTE_NORMAL);
  1507. AsrpPrintDbgMsg(_asrwarn,
  1508. "Setup was unable to display error file, [%ws] failed (0x%x)\r\n",
  1509. onFatalCmd,
  1510. GetLastError()
  1511. );
  1512. }
  1513. _AsrFree(onFatalCmd);
  1514. }
  1515. BOOL
  1516. AsrpSetFileSecurity(
  1517. )
  1518. {
  1519. DWORD dwStatus = ERROR_SUCCESS;
  1520. SECURITY_ATTRIBUTES securityAttributes;
  1521. SECURITY_DESCRIPTOR securityDescriptor;
  1522. BOOL bResult = FALSE;
  1523. if ((!Gbl_AsrErrorFilePath) || (!Gbl_AsrLogFilePath)) {
  1524. SetLastError(ERROR_FILE_NOT_FOUND);
  1525. AsrpPrintDbgMsg(_asrlog,
  1526. "Unable to set backup operator permissions for log/error files (0x2)\r\n");
  1527. return FALSE;
  1528. }
  1529. securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  1530. securityAttributes.lpSecurityDescriptor = &securityDescriptor;
  1531. securityAttributes.bInheritHandle = FALSE;
  1532. bResult = AsrpConstructSecurityAttributes(&securityAttributes, esatFile, TRUE);
  1533. _AsrpErrExitCode((!bResult), dwStatus, GetLastError());
  1534. bResult = SetFileSecurity(Gbl_AsrErrorFilePath,
  1535. DACL_SECURITY_INFORMATION,
  1536. &securityDescriptor
  1537. );
  1538. _AsrpErrExitCode((!bResult), dwStatus, GetLastError());
  1539. AsrpPrintDbgMsg(_asrinfo,
  1540. "Set backup operator permissions for error file at %ws\r\n",
  1541. Gbl_AsrErrorFilePath
  1542. );
  1543. bResult = SetFileSecurity(Gbl_AsrLogFilePath,
  1544. DACL_SECURITY_INFORMATION,
  1545. &securityDescriptor
  1546. );
  1547. _AsrpErrExitCode((!bResult), dwStatus, GetLastError());
  1548. AsrpPrintDbgMsg(_asrinfo,
  1549. "Set backup operator permissions for log file at %ws\r\n",
  1550. Gbl_AsrLogFilePath
  1551. );
  1552. EXIT:
  1553. AsrpCleanupSecurityAttributes(&securityAttributes);
  1554. if (ERROR_SUCCESS != dwStatus) {
  1555. SetLastError(dwStatus);
  1556. }
  1557. if (bResult) {
  1558. AsrpPrintDbgMsg(_asrinfo, "Set backup operator permissions for files\r\n");
  1559. }
  1560. else {
  1561. AsrpPrintDbgMsg(_asrlog,
  1562. "Unable to set backup operator permissions for log/error files (0x%lu)\r\n",
  1563. GetLastError());
  1564. }
  1565. return bResult;
  1566. }
  1567. ///////////////////////////////////////////////////////////////////////////////
  1568. // Public function definitions.
  1569. ///////////////////////////////////////////////////////////////////////////////
  1570. VOID
  1571. AsrInitialize(VOID)
  1572. /*++
  1573. Description:
  1574. Initializes the data structures required to complete ASR (Automated System
  1575. Recovery, aka Disaster Recovery). This consists of reading the asr.sif
  1576. file then initializing a list of recovery applications to be executed.
  1577. Arguments:
  1578. None.
  1579. Returns:
  1580. None.
  1581. --*/
  1582. {
  1583. PWSTR sifName = NULL;
  1584. HINF sifHandle = NULL;
  1585. BOOL result = FALSE;
  1586. UINT errorLine = 0;
  1587. SYSTEMTIME currentTime;
  1588. GetSystemTime(&currentTime);
  1589. //
  1590. // Set the %TEMP% to c:\temp
  1591. //
  1592. AsrpSetEnvironmentVariables();
  1593. //
  1594. // Initialise the log files
  1595. //
  1596. AsrpInitialiseErrorFile();
  1597. AsrpInitialiseLogFile();
  1598. AsrpPrintDbgMsg(_asrlog,
  1599. "Entering GUI-mode Automated System Recovery. UTC: %04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu.\r\n",
  1600. currentTime.wYear,
  1601. currentTime.wMonth,
  1602. currentTime.wDay,
  1603. currentTime.wHour,
  1604. currentTime.wMinute,
  1605. currentTime.wSecond,
  1606. currentTime.wMilliseconds
  1607. );
  1608. //
  1609. // Open the asr.sif file
  1610. //
  1611. sifName = AsrpExpandEnvStrings(AsrSifPath);
  1612. if (!sifName) {
  1613. AsrpPrintDbgMsg(_asrerror, "Setup was unable to locate the ASR state file asr.sif.\r\n");
  1614. FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0);
  1615. }
  1616. sifHandle = SetupOpenInfFileW(
  1617. sifName,
  1618. NULL, // Inf Class
  1619. INF_STYLE_WIN4,
  1620. &errorLine // Error-line
  1621. );
  1622. if ((!sifHandle) || (INVALID_HANDLE_VALUE == sifHandle)) {
  1623. AsrpPrintDbgMsg(_asrerror,
  1624. "Setup was unable to open the ASR state file [%ws]. Error-code: 0x%x, Line %lu\r\n",
  1625. sifName,
  1626. GetLastError(),
  1627. errorLine
  1628. );
  1629. _AsrFree(sifName);
  1630. FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0);
  1631. }
  1632. //
  1633. // Add the "last instance" registry entry for ASR.
  1634. //
  1635. AsrpAddRegistryEntry();
  1636. //
  1637. // Set the time-zone information.
  1638. //
  1639. result = AsrpRestoreTimeZoneInformation(sifName);
  1640. if (!result) {
  1641. AsrpPrintDbgMsg(_asrwarn,
  1642. "Setup was unable to restore the time-zone information on the machine. (0x%x) ASR state file %ws\r\n",
  1643. GetLastError(),
  1644. (sifName ? sifName : L"could not be determined")
  1645. );
  1646. }
  1647. else {
  1648. AsrpPrintDbgMsg(_asrlog, "Successfully restored time-zone information.\r\n");
  1649. }
  1650. _AsrFree(sifName);
  1651. //AsrpPerformSifIntegrityCheck(Handle); No check at the moment
  1652. //
  1653. // Make sure the licensed processors key is set. I'm adding this call here
  1654. // since if this key isn't present when we reboot, the system bugchecks with
  1655. // 9A: system_license_violation.
  1656. //
  1657. SetEnabledProcessorCount();
  1658. SetupCloseInfFile(sifHandle);
  1659. Gbl_IsAsrEnabled = TRUE;
  1660. }
  1661. BOOL
  1662. AsrIsEnabled(VOID)
  1663. /*++
  1664. Description:
  1665. Informs the caller whether ASR has been enabled by returning the value of
  1666. the Gbl_IsAsrEnabled flag.
  1667. Arguments:
  1668. None.
  1669. Returns:
  1670. TRUE, if ASR is enabled. Otherwise, FALSE is returned.
  1671. --*/
  1672. {
  1673. return Gbl_IsAsrEnabled;
  1674. }
  1675. VOID
  1676. AsrExecuteRecoveryApps(VOID)
  1677. /*++
  1678. Description:
  1679. Executes the commands in the [COMMANDS] section of the asr.sif file.
  1680. Arguments:
  1681. None.
  1682. Returns:
  1683. None.
  1684. --*/
  1685. {
  1686. BOOL errors = FALSE,
  1687. result = FALSE;
  1688. DWORD exitCode = 0;
  1689. LONG criticalApp = 0;
  1690. PWSTR sifPath = NULL;
  1691. PWSTR application = NULL;
  1692. PASR_RECOVERY_APP_NODE pNode = NULL;
  1693. ASR_RECOVERY_APP_LIST list = {NULL, NULL, 0};
  1694. SYSTEMTIME currentTime;
  1695. PWSTR errString = NULL;
  1696. ASSERT_HEAP_IS_VALID();
  1697. //
  1698. // Restore the non-critical disks.
  1699. //
  1700. SetLastError(ERROR_SUCCESS);
  1701. sifPath = AsrpExpandEnvStrings(AsrSifPath);
  1702. if (sifPath) {
  1703. result = AsrpRestoreNonCriticalDisksW(sifPath, TRUE);
  1704. }
  1705. if (!result) {
  1706. AsrpPrintDbgMsg(_asrwarn,
  1707. "Setup was unable to restore the configuration of some of the disks on the machine. (0x%x) ASR state file %ws\r\n",
  1708. GetLastError(),
  1709. (sifPath ? sifPath : L"could not be determined")
  1710. );
  1711. }
  1712. else {
  1713. AsrpPrintDbgMsg(_asrlog,
  1714. "Successfully recreated disk configurations.\r\n");
  1715. }
  1716. _AsrFree(sifPath);
  1717. ASSERT_HEAP_IS_VALID();
  1718. //
  1719. // Close the system handle
  1720. //
  1721. if ((Gbl_AsrSystemVolumeHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrSystemVolumeHandle)) {
  1722. CloseHandle(Gbl_AsrSystemVolumeHandle);
  1723. Gbl_AsrSystemVolumeHandle = NULL;
  1724. AsrpPrintDbgMsg(_asrinfo, "Closed system device handle.\r\n");
  1725. }
  1726. else {
  1727. AsrpPrintDbgMsg(_asrinfo, "Did not have a valid system device handle to close.\r\n");
  1728. }
  1729. //
  1730. // Set the file security for the log and err files, to allow
  1731. // backup operators to be able to access it on reboot
  1732. //
  1733. AsrpSetFileSecurity();
  1734. AsrpInitExecutionEnv(&list);
  1735. //
  1736. // Sort the list of recovery apps, by sequence number.
  1737. //
  1738. AsrpSortAppListBySequenceNumber(&list);
  1739. //
  1740. // Change the boot timeout value in the boot.ini file. We do this now,
  1741. // since executed apps in the list might result in changing drive letter,
  1742. // which would make finding boot.ini non-trivial.
  1743. //
  1744. if (!ChangeBootTimeout(30)) {
  1745. AsrpPrintDbgMsg(_asrwarn, "Failed to change boot.ini timeout value.\r\n");
  1746. }
  1747. //
  1748. // Remove an application from the list and execute it. Continue until
  1749. // no more applications remain.
  1750. //
  1751. pNode = AsrpRemoveFirstNodeFromList(&list);
  1752. while (pNode && !errors) {
  1753. application = AsrpBuildInvocationString(pNode);
  1754. criticalApp = pNode->CriticalApp;
  1755. //
  1756. // We don't need pNode any more
  1757. //
  1758. if (pNode->RecoveryAppParams) {
  1759. _AsrFree(pNode->RecoveryAppParams);
  1760. }
  1761. _AsrFree(pNode->RecoveryAppCommand);
  1762. _AsrFree(pNode);
  1763. //
  1764. // if the cmd line couldn't be created:
  1765. // for a critical app, fail.
  1766. // for a non-critical app, move on to next
  1767. //
  1768. if (!application) {
  1769. if (0 < criticalApp) {
  1770. errors = TRUE;
  1771. }
  1772. }
  1773. else {
  1774. //
  1775. // Launch the app
  1776. //
  1777. AsrpPrintDbgMsg(_asrlog, "Invoking external recovery application [%ws]\r\n", application);
  1778. exitCode = ERROR_SUCCESS;
  1779. SetLastError(ERROR_SUCCESS);
  1780. result = InvokeExternalApplication(
  1781. NULL, // no Application Name
  1782. application, // the full command string
  1783. &exitCode // we want a synchronous execution
  1784. );
  1785. if (!result) {
  1786. AsrpPrintDbgMsg(_asrerror,
  1787. "Setup was unable to start the recovery application \"%ws\" (0x%x).\r\n",
  1788. application,
  1789. GetLastError()
  1790. );
  1791. //
  1792. // If a critical app couldn't be launched, it's a fatal error
  1793. //
  1794. if (0 < criticalApp) {
  1795. errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_LAUNCH_APP);
  1796. if (errString) {
  1797. swprintf(g_szErrorMessage, errString, application, GetLastError());
  1798. AsrpLogErrorMessage(g_szErrorMessage);
  1799. MyFree(errString);
  1800. errString = NULL;
  1801. }
  1802. else {
  1803. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0);
  1804. }
  1805. errors = TRUE;
  1806. }
  1807. }
  1808. else {
  1809. //
  1810. // Application was started: check the return code. If return
  1811. // code is not zero and this is a critical app (ie criticalApp=1)
  1812. // it is a fatal error
  1813. //
  1814. if ((ERROR_SUCCESS != exitCode) && (0 < criticalApp)) {
  1815. AsrpPrintDbgMsg(_asrerror, "The recovery application \"%ws\" returned an error code 0x%x. Since this indicates an unrecoverable error, ASR cannot continue on this machine.\r\n", application, exitCode);
  1816. errString = MyLoadString(IDS_ASR_ERROR_RECOVERY_APP_FAILED);
  1817. if (errString) {
  1818. swprintf(g_szErrorMessage, errString, application, exitCode);
  1819. AsrpLogErrorMessage(g_szErrorMessage);
  1820. MyFree(errString);
  1821. errString = NULL;
  1822. }
  1823. else {
  1824. FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0);
  1825. }
  1826. errors = TRUE;
  1827. }
  1828. else {
  1829. AsrpPrintDbgMsg(_asrlog, "The recovery application \"%ws\" returned an exit code of 0x%x\r\n", application, exitCode);
  1830. }
  1831. }
  1832. _AsrFree(application);
  1833. }
  1834. pNode = AsrpRemoveFirstNodeFromList(&list);
  1835. }
  1836. if (errors) {
  1837. //
  1838. // A critical app above did not return 0.
  1839. //
  1840. AsrpExecuteOnFatalError();
  1841. }
  1842. else {
  1843. //
  1844. // We executed all the apps, without any critical failure.
  1845. //
  1846. RemoveRestartability(NULL);
  1847. DeleteLocalSource();
  1848. AsrpMergeSetupLogIfNeeded();
  1849. AsrpPrintDbgMsg(_asrlog, "ASR completed successfully.\r\n");
  1850. }
  1851. GetSystemTime(&currentTime);
  1852. AsrpPrintDbgMsg(_asrlog,
  1853. "Exiting from GUI-mode Automated System Recovery. UTC: %04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu.\r\n",
  1854. currentTime.wYear,
  1855. currentTime.wMonth,
  1856. currentTime.wDay,
  1857. currentTime.wHour,
  1858. currentTime.wMinute,
  1859. currentTime.wSecond,
  1860. currentTime.wMilliseconds
  1861. );
  1862. //
  1863. // Clean up global values
  1864. //
  1865. AsrpCloseLogFiles();
  1866. ASSERT_HEAP_IS_VALID();
  1867. }