/*++ Copyright (c) 1998 Microsoft Corporation Module Name: setupasr.c Abstract: Services in this module implement the Automatic System Recovery (ASR) routines of guimode setup. Revision History: Initial Code Michael Peterson (v-michpe) 20.Jan.1998 Code cleanup and changes Guhan Suriyanarayanan (guhans) 21.Sep.1999 --*/ #include "setupp.h" #pragma hdrstop #include #include #include #include #define THIS_MODULE 'S' #include "asrpriv.h" /////////////////////////////////////////////////////////////////////////////// // Private Type and constant declarations /////////////////////////////////////////////////////////////////////////////// const PCWSTR AsrSifPath = L"%systemroot%\\repair\\asr.sif\0"; const PCWSTR AsrCommandsSectionName = L"COMMANDS"; const PCWSTR AsrCommandSuffix = L"/sifpath=%systemroot%\\repair\\asr.sif"; const PCWSTR AsrTempDir = L"%systemdrive%\\TEMP"; const PCWSTR AsrLogFileName = L"\\asr.log"; const PCWSTR AsrErrorFileName = L"\\asr.err"; const PCWSTR Asr_ControlAsrRegKey = L"SYSTEM\\CurrentControlSet\\Control\\ASR"; const PCWSTR Asr_LastInstanceRegValue = L"Instance"; // // The following are to update system and boot partition devices // in setup.log // const PCWSTR Asr_SystemDeviceEnvName = L"%ASR_C_SYSTEM_PARTITION_DEVICE%"; const PCWSTR Asr_SystemDeviceWin32Path = L"\\\\?\\GLOBALROOT%ASR_C_SYSTEM_PARTITION_DEVICE%"; const PCWSTR Asr_WinntDeviceEnvName = L"%ASR_C_WINNT_PARTITION_DEVICE%"; const PCWSTR Asr_SetupLogFilePath = L"%systemroot%\\repair\\setup.log"; const PCWSTR Asr_AsrLogFilePath = L"%systemroot%\\repair\\asr.log"; const PCWSTR Asr_AsrErrorFilePath = L"%systemroot%\\repair\\asr.err"; const PCWSTR Asr_OldAsrErrorFilePath = L"%systemroot%\\repair\\asr.err.old"; const PCWSTR Asr_FatalErrorCommand = L"notepad.exe %systemroot%\\repair\\asr.err"; /////////////////////////////////////////////////////////////////////////////// // Data global to this module /////////////////////////////////////////////////////////////////////////////// BOOL Gbl_IsAsrEnabled = FALSE; PWSTR Gbl_AsrErrorFilePath = NULL; PWSTR Gbl_AsrLogFilePath = NULL; HANDLE Gbl_AsrLogFileHandle = NULL; HANDLE Gbl_AsrSystemVolumeHandle = NULL; WCHAR g_szErrorMessage[4196]; /////////////////////////////////////////////////////////////////////////////// // Macros /////////////////////////////////////////////////////////////////////////////// // // ASR Memory allocation and free wrappers // // // _AsrAlloc // Macro description: // ASSERTS first if ptr is non-NULL. The expectation is that // all ptrs must be initialised to NULL before they are allocated. // That way, we can catch instances where we try to re-allocate // memory without freeing first. // // IsNullFatal: flag to indicate if mem allocation failures are fatal // #define _AsrAlloc(ptr,sz,IsNullFatal) { \ \ if (ptr != NULL) { \ AsrpPrintDbgMsg(_asrinfo, "Pointer being allocated not NULL.\r\n"); \ MYASSERT(0); \ } \ \ ptr = MyMalloc(sz); \ \ if (ptr) { \ memset(ptr, 0, sz); \ } \ \ if (!ptr) { \ if ((BOOLEAN) IsNullFatal) { \ AsrpPrintDbgMsg(_asrerror, "Setup was unable to allocate memory.\r\n"); \ FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); \ } \ else { \ AsrpPrintDbgMsg(_asrwarn, "Warning. Setup was unable to allocate memory.\r\n"); \ } \ } \ } // // _AsrFree // Macro description: // Frees ptr and resets it to NULL. // Asserts if ptr was already NULL // #define _AsrFree(ptr) { \ \ if (NULL != ptr) { \ MyFree(ptr); \ ptr = NULL; \ } \ else { \ AsrpPrintDbgMsg(_asrlog, "Attempt to free null Pointer.\r\n"); \ MYASSERT(0); \ } \ } #define _AsrFreeIfNotNull(ptr) { \ if (NULL != ptr) { \ MyFree(ptr); \ ptr = NULL; \ } \ } // // One ASR_RECOVERY_APP_NODE struct is created for each entry // in the [COMMANDS] section of asr.sif. // typedef struct _ASR_RECOVERY_APP_NODE { struct _ASR_RECOVERY_APP_NODE *Next; // // Expect this to always be 1 // LONG SystemKey; // // The sequence number according to which the apps are run. If // two apps have the same sequence number, the app that appears // first in the sif file is run. // LONG SequenceNumber; // // The "actionOnCompletion" field for the app. If CriticalApp is // non-zero, and the app returns an non-zero exit-code, we shall // consider it a fatal failure and quit out of ASR. // LONG CriticalApp; // // The app to be launched // PWSTR RecoveryAppCommand; // // The paramaters for the app. This is just concatenated to the // string above. May be NULL. // PWSTR RecoveryAppParams; } ASR_RECOVERY_APP_NODE, *PASR_RECOVERY_APP_NODE; // // This contains our list of entries in the COMMANDS section, // sorted in order of sequence numbers. // typedef struct _ASR_RECOVERY_APP_LIST { PASR_RECOVERY_APP_NODE First; // Head PASR_RECOVERY_APP_NODE Last; // Tail LONG AppCount; // NumEntries } ASR_RECOVERY_APP_LIST, *PASR_RECOVERY_APP_LIST; // // We call this to change the boot.ini timeout value to 30 seconds // extern BOOL ChangeBootTimeout(IN UINT Timeout); // // From asr.c // extern BOOL AsrpRestoreNonCriticalDisksW( IN PCWSTR lpSifPath, IN BOOL bAllOrNothing ); extern BOOL AsrpRestoreTimeZoneInformation( IN PCWSTR lpSifPath ); // // Indices for fields in the [COMMANDS] section. // typedef enum _SIF_COMMANDS_FIELD_INDEX { ASR_SIF_COMMANDS_KEY = 0, ASR_SIF_SYSTEM_KEY, // Expected to always be "1" ASR_SIF_SEQUENCE_NUMBER, ASR_SIF_ACTION_ON_COMPLETION, ASR_SIF_COMMAND_STRING, ASR_SIF_COMMAND_PARAMETERS, // May be NULL SIF_SIF_NUMFIELDS // Must always be last } SIF_COMMANDS_FIELD_INDEX; #define _Asr_CHECK_BOOLEAN(b,msg) \ if((b) == FALSE) { \ AsrpFatalErrorExit(MSG_FATAL_ERROR, __LINE__, (msg)); \ } /////////////////////////////////////////////////////////////////////////////// // Private Functions /////////////////////////////////////////////////////////////////////////////// // // Logs the message to the asr error file. Note that // AsrpInitialiseErrorFile must have been called once before // this routine is used. // VOID AsrpLogErrorMessage( IN PCWSTR buffer ) { HANDLE hFile = NULL; DWORD bytesWritten = 0; if (Gbl_AsrErrorFilePath) { // // Open the error log // hFile = CreateFileW( Gbl_AsrErrorFilePath, // lpFileName GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode NULL, // lpSecurityAttributes OPEN_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((!hFile) || (INVALID_HANDLE_VALUE == hFile)) { return; } // // Move to the end of file // SetFilePointer(hFile, 0L, NULL, FILE_END); // // Add our error string // WriteFile(hFile, buffer, (wcslen(buffer) * sizeof(WCHAR)), &bytesWritten, NULL ); // // And we're done // CloseHandle(hFile); } } // // Logs the message to the asr log file. Note that // AsrpInitialiseLogFile must have been called once before // this routine is used. // VOID AsrpLogMessage( IN CONST char Module, IN CONST ULONG Line, IN CONST ULONG MesgLevel, IN CONST PCSTR Message ) { SYSTEMTIME currentTime; DWORD bytesWritten = 0; char buffer[4196]; GetSystemTime(¤tTime); sprintf(buffer, "[%04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu] %c%lu %s%s", currentTime.wYear, currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond, currentTime.wMilliseconds, Module, Line, ((DPFLTR_ERROR_LEVEL == MesgLevel) ? "(Error:ASR) " : (DPFLTR_WARNING_LEVEL == MesgLevel ? "(Warning:ASR) " : "")), Message ); if (Gbl_AsrLogFileHandle) { WriteFile(Gbl_AsrLogFileHandle, buffer, (strlen(buffer) * sizeof(char)), &bytesWritten, NULL ); } } VOID AsrpPrintDbgMsg( IN CONST char Module, IN CONST ULONG Line, IN CONST ULONG MesgLevel, IN PCSTR FormatString, ...) /*++ Description: This prints a debug message AND makes the appropriate entries in the log and error files. Arguments: Line pass in __LINE__ MesgLevel DPFLTR_ levels FormatString Formatted Message String to be printed. Returns: --*/ { char str[4096]; // the message better fit in this va_list arglist; DbgPrintEx(DPFLTR_SETUP_ID, MesgLevel, "ASR %c%lu ", Module, Line); va_start(arglist, FormatString); wvsprintfA(str, FormatString, arglist); va_end(arglist); DbgPrintEx(DPFLTR_SETUP_ID, MesgLevel, str); if ((DPFLTR_ERROR_LEVEL == MesgLevel) || (DPFLTR_WARNING_LEVEL == MesgLevel) || (DPFLTR_TRACE_LEVEL == MesgLevel) ) { AsrpLogMessage(Module, Line, MesgLevel, str); } } // // This will terminate Setup and cause a reboot. This is called // on Out of Memory errors // VOID AsrpFatalErrorExit( IN LONG MsgValue, IN LONG LineNumber, IN PWSTR MessageString ) { AsrpPrintDbgMsg(THIS_MODULE, LineNumber, DPFLTR_ERROR_LEVEL, "Fatal Error: %ws (%lu)", (MessageString ? MessageString : L"(No error string)"), GetLastError() ); FatalError(MsgValue, MessageString, 0, 0); } // // This just adds the new node to the end of the list. // Note that this does NOT sort the list by sequenceNumber: // we'll do that later on // VOID AsrpAppendNodeToList( IN PASR_RECOVERY_APP_LIST pList, IN PASR_RECOVERY_APP_NODE pNode ) { // // Insert at end of list. // pNode->Next = NULL; if (pList->AppCount == 0) { pList->First = pNode; } else { pList->Last->Next = pNode; } pList->Last = pNode; pList->AppCount += 1; } // // Pops off the first node in the list. The list is sorted // in order of increasing SequenceNumber's at this point. // PASR_RECOVERY_APP_NODE AsrpRemoveFirstNodeFromList( IN PASR_RECOVERY_APP_LIST pList ) { PASR_RECOVERY_APP_NODE pNode; if(pList->AppCount == 0) { return NULL; } pNode = pList->First; pList->First = pNode->Next; pList->AppCount -= 1; MYASSERT(pList->AppCount >= 0); return pNode; } PWSTR // must be freed by caller AsrpExpandEnvStrings( IN CONST PCWSTR OriginalString ) { PWSTR expandedString = NULL; UINT cchSize = MAX_PATH + 1, // start with a reasonable default cchRequiredSize = 0; BOOL result = FALSE; _AsrAlloc(expandedString, (cchSize * sizeof(WCHAR)), TRUE); cchRequiredSize = ExpandEnvironmentStringsW(OriginalString, expandedString, cchSize ); if (cchRequiredSize > cchSize) { // // Buffer wasn't big enough; free and re-allocate as needed // _AsrFree(expandedString); cchSize = cchRequiredSize + 1; _AsrAlloc(expandedString, (cchSize * sizeof(WCHAR)), TRUE); cchRequiredSize = ExpandEnvironmentStringsW(OriginalString, expandedString, cchSize ); } if ((0 == cchRequiredSize) || (cchRequiredSize > cchSize)) { // // Either the function failed, or the buffer wasn't big enough // even on the second try // _AsrFree(expandedString); // sets it to NULL } return expandedString; } // // Builds the invocation string, as the name suggests. It expands out // the environment variables that apps are allowed to use in the // sif file, and adds in /sifpath= at the end // of the command. So for an entry in the COMMANDS section of // the form: // 4=1,3500,0,"%TEMP%\app.exe","/param1 /param2" // // the invocation string would be of the form: // c:\windows\temp\app.exe /param1 /param2 /sifpath=c:\windows\repair\asr.sif // // PWSTR AsrpBuildInvocationString( IN PASR_RECOVERY_APP_NODE pNode // must not be NULL ) { PWSTR app = pNode->RecoveryAppCommand, args = pNode->RecoveryAppParams, cmd = NULL, fullcmd = NULL; DWORD size = 0; MYASSERT(app); // // Build an command line that looks like... // // "%TEMP%\ntbackup recover /1 /sifpath=%systemroot%\repair\asr.sif" // // The /sifpath parameter is added to all apps being launched // // // Allocate memory for the cmd line // size = sizeof(WCHAR) * ( wcslen(app) + // app name "%TEMP%\ntbackup" (args ? wcslen(args) : 0) + // arguments "recover /1" wcslen(AsrCommandSuffix) + // suffix "/sifpath=%systemroot%\repair\asr.sif" 4 // spaces and null ); _AsrAlloc(cmd, size, TRUE); // won't return if alloc fails // // Build the string // swprintf(cmd, L"%ws %ws %ws", app, (args? args: L""), AsrCommandSuffix ); // // Expand the %% stuff, to build the full path // fullcmd = AsrpExpandEnvStrings(cmd); _AsrFree(cmd); return fullcmd; } BOOL AsrpRetryIsServiceRunning( IN PWSTR ServiceName, IN UINT MaxRetries ) { SERVICE_STATUS status; SC_HANDLE svcHandle = NULL, // handle to the service scmHandle = NULL; // handle to the service control manager UINT count = 0; BOOL errorsEncountered = FALSE; PWSTR errString = NULL; scmHandle = OpenSCManager(NULL, NULL, GENERIC_READ); if (!scmHandle) { // // OpenSCManager() call failed - we are broke. // AsrpPrintDbgMsg(_asrerror, "Setup was unable to open the service control manager. The error code returned was 0x%x.\r\n", GetLastError() ); errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_OPEN_SCM); if (errString) { swprintf(g_szErrorMessage, errString, GetLastError()); AsrpLogErrorMessage(g_szErrorMessage); MyFree(errString); errString = NULL; } else { FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); } errorsEncountered = TRUE; goto EXIT; } svcHandle = OpenServiceW(scmHandle, ServiceName, SERVICE_QUERY_STATUS); if (!svcHandle) { // // OpenService() call failed - we are broke. // AsrpPrintDbgMsg(_asrerror, "Setup was unable to start the service \"%ws\". The error code returned was 0x%x.\r\n", ServiceName, GetLastError() ); errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_START_SERVICE); if (errString) { swprintf(g_szErrorMessage, errString, ServiceName, GetLastError()); AsrpLogErrorMessage(g_szErrorMessage); MyFree(errString); errString = NULL; } else { FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); } errorsEncountered = TRUE; goto EXIT; } // // Got the service opened for query. See if it's running, and // if not, go thru the retry loop. // while (count < MaxRetries) { if (!QueryServiceStatus(svcHandle, &status)) { // // Couldn't query the status of the service // AsrpPrintDbgMsg(_asrerror, "Setup was unable to query the status of service \"%ws\". The error code returned was 0x%x\r\n", ServiceName, GetLastError() ); errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_START_SERVICE); if (errString) { swprintf(g_szErrorMessage, errString, ServiceName, GetLastError()); AsrpLogErrorMessage(g_szErrorMessage); MyFree(errString); errString = NULL; } else { FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); } errorsEncountered = TRUE; goto EXIT; } if (status.dwCurrentState == SERVICE_RUNNING) { // // Service is running - we can proceed. // break; } ++count; AsrpPrintDbgMsg(_asrinfo, "Attempting to start service [%ws]: status = [%d], retry [%d]\r\n", ServiceName, status.dwCurrentState, count ); Sleep(2000); } EXIT: if ((svcHandle) && (INVALID_HANDLE_VALUE != svcHandle)) { CloseServiceHandle(svcHandle); svcHandle = NULL; } if ((scmHandle) && (INVALID_HANDLE_VALUE != svcHandle)) { CloseServiceHandle(scmHandle); scmHandle = NULL; } if ((errorsEncountered) || (count >= MaxRetries)) { return FALSE; } else { return TRUE; } } // // Before launching apps, we need RSM (specifically, the backup app // might need RSM to access its backup media) // VOID AsrpStartNtmsService(VOID) { BOOL result = TRUE; DWORD exitCode = ERROR_SUCCESS; PWSTR registerNtmsCommand = NULL; AsrpPrintDbgMsg(_asrinfo, "Entered InitNtmsService()\r\n"); // // RSM isn't setup to run during GUI mode setup, but the back-up app is // likely going to need access to tape-drives and other RSM devices. // So we regsvr32 the appropriate dll's and start the service // // Register the rsmps.dll using: // regsvr32 /s %Systemroot%\system32\rsmps.dll // result = FALSE; registerNtmsCommand = AsrpExpandEnvStrings(L"regsvr32 /s %systemroot%\\system32\\rsmps.dll"); if (registerNtmsCommand) { result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode); AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand); _AsrFree(registerNtmsCommand); } _Asr_CHECK_BOOLEAN(result, L"regsvr32 /s %systemroot%\\rsmps.dll failed\r\n"); // // Register the rsmmllsv.exe using: // %SystemRoot%\system32\rsmmllsv.exe /regserver // result = FALSE; registerNtmsCommand = AsrpExpandEnvStrings(L"%systemroot%\\system32\\rsmmllsv.exe /regserver"); if (registerNtmsCommand) { result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode); AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand); _AsrFree(registerNtmsCommand); } _Asr_CHECK_BOOLEAN(result, L"%systemroot%\\system32\\rsmmllsv.exe /regserver failed\r\n"); // // Register the ntmssvc.dll using: // regsvr32 /s %SystemRoot%\system32\ntmssvc.dll // result = FALSE; registerNtmsCommand = AsrpExpandEnvStrings(L"regsvr32 /s %systemroot%\\system32\\ntmssvc.dll"); if (registerNtmsCommand) { result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode); AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand); _AsrFree(registerNtmsCommand); } _Asr_CHECK_BOOLEAN(result, L"regsvr32 /s %systemroot%\\ntmssvc.dll failed\r\n"); // // Register the rsmsink.exe using: // %SystemRoot%\system32\rsmsink.exe /regserver // result = FALSE; registerNtmsCommand = AsrpExpandEnvStrings(L"%systemroot%\\system32\\rsmsink.exe /regserver"); if (registerNtmsCommand) { result = InvokeExternalApplication(NULL, registerNtmsCommand, &exitCode); AsrpPrintDbgMsg(_asrlog, "Executed [%ws]\r\n", registerNtmsCommand); _AsrFree(registerNtmsCommand); } _Asr_CHECK_BOOLEAN(result, L"%systemroot%\\system32\\rsmsink.exe /regserver failed\r\n"); // // Now, start the ntms service. // result = SetupStartService(L"ntmssvc", FALSE); _Asr_CHECK_BOOLEAN(result, L"Could not start RSM service (ntmssvc).\r\n"); // // Check for ntms running, give a few retries. // result = AsrpRetryIsServiceRunning(L"ntmssvc", 30); _Asr_CHECK_BOOLEAN(result, L"Failed to start RSM service after 30 retries.\r\n"); AsrpPrintDbgMsg(_asrinfo, "RSM service (ntmssvc) started.\r\n"); } PWSTR AsrpReadField( PINFCONTEXT pInfContext, DWORD FieldIndex, BOOL NullOkay ) { PWSTR data = NULL; UINT reqdSize = 0; BOOL result = FALSE; // // Allocate memory and read the data // _AsrAlloc(data, (sizeof(WCHAR) * (MAX_PATH + 1)), TRUE); result = SetupGetStringFieldW( pInfContext, FieldIndex, data, MAX_PATH + 1, &reqdSize ); if (!result) { DWORD status = GetLastError(); // // If our buffer was too small, allocate a larger buffer // and try again // if (ERROR_INSUFFICIENT_BUFFER == status) { status = ERROR_SUCCESS; _AsrFree(data); _AsrAlloc(data, (sizeof(WCHAR) * reqdSize), TRUE); result = SetupGetStringFieldW( pInfContext, FieldIndex, data, reqdSize, NULL // don't need required size any more ); } } if (!result) { _AsrFree(data); _Asr_CHECK_BOOLEAN(NullOkay, L"Could not read entry from commands section"); // Never returns if NullOkay is FALSE. // Memory leaks here then, since we don't free some structs. But // it's a fatal error, so the system must be rebooted anyway // } return data; } // // This adds in the "Instance" value under the ASR key. // Third party applications (or Windows components like DTC) can use // this to determine if a new ASR has been run since the last time we // booted, and can take any actions they need to. For instance, the // DTC log file needs to be recreated after an ASR, since it is not // backed-up or restored by the backup app, and Dtc refuses to start // if it doesn't find a log file when it expects one. // VOID AsrpAddRegistryEntry() { LONG result = 0; HKEY regKey = NULL; WCHAR szLastInstanceData[40]; DWORD cbLastInstanceData = 0; SYSTEMTIME currentTime; GUID asrInstanceGuid; PWSTR lpGuidString = NULL; RPC_STATUS rpcStatus = RPC_S_OK; // // We try to set the key to a newly generated GUID, to make sure it is // unique (and different from the previous value stored there). If, for // some reason, we aren't able to generate a GUID, we'll just store the // current date and time as a string--that should be unique, too. // rpcStatus = UuidCreate( &asrInstanceGuid ); if (RPC_S_OK == rpcStatus) { // // Convert the GUID to a printable string // rpcStatus = UuidToStringW( &asrInstanceGuid, &lpGuidString ); if (RPC_S_OK == rpcStatus) { wsprintf(szLastInstanceData, L"%ws", lpGuidString ); cbLastInstanceData = wcslen(szLastInstanceData)*sizeof(WCHAR); } if (lpGuidString) { RpcStringFreeW(&lpGuidString); } } if (RPC_S_OK != rpcStatus) { // // We couldn't get a GUID. Let's store the time-stamp ... // GetSystemTime(¤tTime); wsprintf(szLastInstanceData, L"%04hu%02hu%02hu%02hu%02hu%02hu%03hu", currentTime.wYear, currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond, currentTime.wMilliseconds ); cbLastInstanceData = wcslen(szLastInstanceData)*sizeof(WCHAR); } result = RegCreateKeyExW( HKEY_LOCAL_MACHINE, // hKey Asr_ControlAsrRegKey, // lpSubKey 0, // reserved NULL, // lpClass REG_OPTION_NON_VOLATILE, // dwOptions MAXIMUM_ALLOWED, // samDesired NULL, // lpSecurityAttributes ®Key, // phkResult NULL // lpdwDisposition ); if ((ERROR_SUCCESS != result) || (!regKey)) { AsrpPrintDbgMsg(_asrwarn, "Could not create the Control\\ASR registry entry (0x%x).\r\n", result ); return; } result = RegSetValueExW( regKey, // hKey Asr_LastInstanceRegValue, // lpValueName 0L, // reserved REG_SZ, // dwType (LPBYTE)szLastInstanceData, // lpData cbLastInstanceData // cbData ); RegCloseKey(regKey); if (ERROR_SUCCESS != result) { AsrpPrintDbgMsg(_asrwarn, "Could not set the ASR instance-ID in the registry (0x%x).\r\n", result ); return; } AsrpPrintDbgMsg(_asrlog, "Set the ASR instance-ID at [%ws\\%ws] value to [%ws]\r\n", Asr_ControlAsrRegKey, Asr_LastInstanceRegValue, szLastInstanceData ); } VOID AsrpSetEnvironmentVariables() { PWSTR TempPath = AsrpExpandEnvStrings(AsrTempDir); if (NULL == TempPath) { return; } if (!CreateDirectoryW(TempPath, NULL)) { AsrpPrintDbgMsg(_asrwarn, "Unable to create TEMP directory [%ws] (%lu)\r\n", TempPath, GetLastError() ); } AsrpPrintDbgMsg(_asrlog, "Setting environment variables TEMP and TMP to [%ws]\r\n", TempPath ); if (!SetEnvironmentVariableW(L"TEMP", TempPath)) { AsrpPrintDbgMsg(_asrwarn, "Unable to set environment variable TEMP to [%ws] (%lu)\r\n", TempPath, GetLastError() ); } if (!SetEnvironmentVariableW(L"TMP", TempPath)) { AsrpPrintDbgMsg(_asrwarn, "Unable to set environment variable TEMP to [%ws] (%lu)\r\n", TempPath, GetLastError() ); } _AsrFree(TempPath); return; } VOID AsrpInitExecutionEnv( OUT PASR_RECOVERY_APP_LIST List ) { PWSTR stateFileName = NULL; HINF sifHandle = NULL; LONG lineCount = 0, line = 0; BOOL result = FALSE; INFCONTEXT infContext; // // Start the RSM service // AsrpStartNtmsService(); // // Open the asr.sif file and build the list // of commands to be launched. // stateFileName = AsrpExpandEnvStrings(AsrSifPath); if (!stateFileName) { AsrpPrintDbgMsg(_asrerror, "Setup was unable to locate the ASR state file asr.sif on this machine.\r\n"); FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0); } sifHandle = SetupOpenInfFileW( stateFileName, NULL, // Inf Class INF_STYLE_WIN4, NULL // Error-line ); if ((!sifHandle) || (INVALID_HANDLE_VALUE == sifHandle)) { AsrpPrintDbgMsg(_asrerror, "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", stateFileName, GetLastError()); _AsrFree(stateFileName); FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0); } _AsrFree(stateFileName); // // Read the COMMANDS section, and add each command to our list // lineCount = SetupGetLineCountW(sifHandle, AsrCommandsSectionName); for (line = 0; line < lineCount; line++) { // // Create a new node // PASR_RECOVERY_APP_NODE pNode = NULL; _AsrAlloc(pNode, (sizeof(ASR_RECOVERY_APP_NODE)), TRUE); // // Get the inf context for the line in asr.sif. This will be used // to read the fields on that line // result = SetupGetLineByIndexW( sifHandle, AsrCommandsSectionName, line, &infContext ); _Asr_CHECK_BOOLEAN(result, L"SetupGetLinebyIndex failed"); // // Read in the int fields // result = SetupGetIntField( &infContext, ASR_SIF_SYSTEM_KEY, &(pNode->SystemKey) ); _Asr_CHECK_BOOLEAN(result, L"could not get system key in commands section"); result = SetupGetIntField( &infContext, ASR_SIF_SEQUENCE_NUMBER, &(pNode->SequenceNumber) ); _Asr_CHECK_BOOLEAN(result, L"could not get sequence number in commands section"); result = SetupGetIntField( &infContext, ASR_SIF_ACTION_ON_COMPLETION, &(pNode->CriticalApp) ); _Asr_CHECK_BOOLEAN(result, L"could not get criticalApp in commands section"); // // Read in the string fields // pNode->RecoveryAppCommand = AsrpReadField( &infContext, ASR_SIF_COMMAND_STRING, FALSE // Null not okay ); pNode->RecoveryAppParams = AsrpReadField( &infContext, ASR_SIF_COMMAND_PARAMETERS, TRUE // Null okay ); // // Add this node to our list, and move on to next // AsrpAppendNodeToList(List, pNode); } SetupCloseInfFile(sifHandle); } // // Bubble sort ... // VOID AsrpSortAppListBySequenceNumber(PASR_RECOVERY_APP_LIST pList) { PASR_RECOVERY_APP_NODE pCurr = NULL, pNext = NULL, *ppPrev = NULL; BOOLEAN done = FALSE; if ((!pList) || (!pList->First)) { MYASSERT(0 && L"Recovery App List pList is NULL"); return; } // // Start the outer loop. Each iteration of the outer loop includes a // full pass down the list, and runs until the inner loop is satisfied // that no more passes are needed. // while (!done) { // // Start at the beginning of the list for each inner (node) loop. // // We will initialize a pointer *to the pointer* which points to // the current node - this pointer might be the address of the "list // first" pointer (as it always will be at the start of an inner loop), // or as the inner loop progresses, it might be the address of the // "next" pointer in the previous node. In either case, the pointer // to which ppPrev points will be changed in the event of a node swap. // pCurr = pList->First; ppPrev = &(pList->First); done = TRUE; MYASSERT(pCurr); while (TRUE) { pNext = pCurr->Next; // // If the current node is the last one, reset to the beginning // and break out to start a new inner loop. // if (pNext == NULL) { pCurr = pList->First; break; } // // If the node *after* the current node has a lower sequence // number, fix up the pointers to swap the two nodes. // if (pCurr->SequenceNumber > pNext->SequenceNumber) { done = FALSE; pCurr->Next = pNext->Next; pNext->Next = pCurr; *ppPrev = pNext; ppPrev = &(pNext->Next); } else { ppPrev = &(pCurr->Next); pCurr = pCurr->Next; } } } } VOID AsrpPerformSifIntegrityCheck(IN HINF Handle) { // // No check for now. // return; } // // This checks if the following entries are different in setup.log // from their values. This could happen because we might have installed // to a new disk that has a different disk number // // [Paths] // TargetDevice = "\Device\Harddisk0\Partition2" // SystemPartition = "\Device\Harddisk0\Partition1" // // If they are different, we'll update them. // BOOL AsrpCheckSetupLogDeviceEntries( PWSTR CurrentSystemDevice, // used for SystemPartition PWSTR CurrentBootDevice, // used for TargetDevice PWSTR LogFileName // path to setup.log ) { WCHAR szLine[MAX_INF_STRING_LENGTH + 1]; PWSTR lpLine = NULL; BOOL isDifferent = FALSE; FILE *fp = NULL; INT iNumEntries = 0; // // Open existing setup.log // fp = _wfopen(LogFileName, L"r"); if (!fp) { AsrpPrintDbgMsg(_asrwarn, "Could not open setup log file [%ws]\r\n", LogFileName ); return FALSE; } // // Check each line of the file for the System or Boot device entries // lpLine = fgetws(szLine, MAX_PATH-1, fp); while ((lpLine) && (iNumEntries < 2)) { BOOL systemEntry = FALSE; BOOL bootEntry = FALSE; if (wcsstr(szLine, L"SystemPartition =")) { systemEntry = TRUE; iNumEntries++; } if (wcsstr(szLine, L"TargetDevice =")) { bootEntry = TRUE; iNumEntries++; } if (systemEntry || bootEntry) { PWSTR DeviceName = NULL; // // Both the system and boot entries must have the full // devicepath in them, of the form \Device\Harddisk0\Partition1 // DeviceName = wcsstr(szLine, L"\\Device"); if (!DeviceName) { isDifferent = TRUE; AsrpPrintDbgMsg(_asrlog, "Marking setup logs different: \\Device\\ not found in boot or system entry\r\n" ); break; } else { // // Find the start of the "Hardisk0\Partition1" text after \Device // PWSTR ss = wcsstr(DeviceName, L"\""); if (!ss) { isDifferent = TRUE; AsrpPrintDbgMsg(_asrlog, "Marking setup logs different: \\Device\\ not found in boot or system entry\r\n" ); break; } else { ss[0] = L'\0'; } } // // And check if this device matches // if (systemEntry) { AsrpPrintDbgMsg(_asrinfo, "Comparing System Device. Current:[%ws] setup.log:[%ws]\r\n", CurrentSystemDevice, DeviceName ); if (wcscmp(DeviceName, CurrentSystemDevice) != 0) { isDifferent = TRUE; AsrpPrintDbgMsg(_asrlog, "System Device has changed. Current:[%ws] setup.log:[%ws]\r\n", CurrentSystemDevice, DeviceName ); break; } } else if (bootEntry) { AsrpPrintDbgMsg(_asrinfo, "Comparing Boot Device. Current:[%ws] setup.log:[%ws]\r\n", CurrentBootDevice, DeviceName ); if (wcscmp(DeviceName, CurrentBootDevice) != 0) { isDifferent = TRUE; AsrpPrintDbgMsg(_asrlog, "Boot device has changed. Current:[%ws] setup.log:[%ws]\r\n", CurrentBootDevice, DeviceName ); break; } } } lpLine = fgetws(szLine, MAX_PATH-1, fp); } if (!isDifferent) { AsrpPrintDbgMsg(_asrinfo, "No changes in system and boot devices for setup.log\r\n"); } fclose(fp); fp = NULL; return isDifferent; } // // If the setup.log restored by the backup from tape has a different // boot or system device marked (we might have picked a new disk in // textmode Setup), this will update the relevant entries to match the // current boot and system devices. // VOID AsrpMergeSetupLog( PWSTR CurrentSystemDevice, PWSTR CurrentBootDevice, PWSTR LogFileName ) { WCHAR szLine[MAX_INF_STRING_LENGTH + 1]; PWSTR lpLine = NULL, lpOldFileName = NULL, lpNewFileName = NULL; BOOL result = FALSE; FILE *fpNew = NULL, *fpCurrent = NULL; INT iNumEntries = 0; // // Create the "new" and "old" file names, i.e., "setup.log.new" and "setup.log.old" // _AsrAlloc(lpNewFileName, ((wcslen(LogFileName) + 5) * sizeof(WCHAR)), TRUE) wcscpy(lpNewFileName, LogFileName); wcscat(lpNewFileName, L".new"); _AsrAlloc(lpOldFileName, ((wcslen(LogFileName) + 5) * sizeof(WCHAR)), TRUE); wcscpy(lpOldFileName, LogFileName); wcscat(lpOldFileName, L".old"); // // Open the current setup.log file. // fpCurrent = _wfopen(LogFileName, L"r"); if (!fpCurrent) { AsrpPrintDbgMsg(_asrwarn, "Setup was unable to open the setup log file \"%ws\"\r\n", LogFileName); goto EXIT; } // // Open the new file - we will write into this one. // fpNew = _wfopen(lpNewFileName, L"w"); if (!fpNew) { AsrpPrintDbgMsg(_asrwarn, "Setup was unable to open the setup log file \"%ws\"\r\n", lpNewFileName); goto EXIT; } // // Read each line in the log file, copy into the new file, unless we hit // one of the two lines in question. Once we've seen both of them, don't // check for them anymore. // lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent); while (lpLine) { BOOL systemEntry = FALSE; BOOL bootEntry = FALSE; // // If we've already found both entries of interest, just copy // and continue. // if (iNumEntries >= 2) { fputws(szLine, fpNew); lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent); continue; } // // Is this line either the boot or system device? // if (wcsstr(szLine, L"SystemPartition =")) { AsrpPrintDbgMsg(_asrlog, "Changing SystemPartition in setup.log to %ws\r\n", CurrentSystemDevice ); ++iNumEntries; wcscpy(szLine, L"SystemPartition = \""); wcscat(szLine, CurrentSystemDevice); wcscat(szLine, L"\"\n"); } else if (wcsstr(szLine, L"TargetDevice =")) { AsrpPrintDbgMsg(_asrlog, "Changing TargetDevice in setup.log to %ws\r\n", CurrentBootDevice ); ++iNumEntries; wcscpy(szLine, L"TargetDevice = \""); wcscat(szLine, CurrentBootDevice); wcscat(szLine, L"\"\n"); } fputws(szLine, fpNew); lpLine = fgetws(szLine, MAX_INF_STRING_LENGTH, fpCurrent); } // // Rename the current setup.log to setup.log.old, and setup.log.new to // setup.log. Need to delay this until reboot since setup.log is in // use. // result = MoveFileExW(LogFileName, lpOldFileName, MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT ); if (!result) { AsrpPrintDbgMsg(_asrwarn, "MoveFileEx([%ws] to [%ws]) failed (%lu)", LogFileName, lpOldFileName, GetLastError() ); } else { result = MoveFileExW(lpNewFileName, LogFileName, MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT ); if (!result) { AsrpPrintDbgMsg(_asrwarn, "MoveFileEx([%ws] to [%ws]) failed (%lu)", lpNewFileName, LogFileName, GetLastError() ); } } EXIT: if (fpCurrent) { fclose(fpCurrent); fpCurrent = NULL; } if (fpNew) { fclose(fpNew); fpNew = NULL; } _AsrFree(lpNewFileName); _AsrFree(lpOldFileName); } VOID AsrpMergeSetupLogIfNeeded() { PWSTR currentSystemDevice = NULL, currentBootDevice = NULL, winntRootDir = NULL, setupLogFileName = NULL; BOOL isSetupLogDifferent = FALSE; // // Get the environment variable for the partition devices // currentSystemDevice = AsrpExpandEnvStrings(Asr_SystemDeviceEnvName); currentBootDevice = AsrpExpandEnvStrings(Asr_WinntDeviceEnvName); setupLogFileName = AsrpExpandEnvStrings(Asr_SetupLogFilePath); if ((!currentSystemDevice) || (!currentBootDevice) || (!setupLogFileName)) { goto EXIT; } // // Check if the system and/or boot devices listed in setup.log are // different than the current devices // isSetupLogDifferent = AsrpCheckSetupLogDeviceEntries( currentSystemDevice, currentBootDevice, setupLogFileName ); if (isSetupLogDifferent) { // // They are different: fix it. // AsrpMergeSetupLog(currentSystemDevice, currentBootDevice, setupLogFileName ); } EXIT: _AsrFreeIfNotNull(setupLogFileName); _AsrFreeIfNotNull(currentBootDevice); _AsrFreeIfNotNull(currentSystemDevice); } // // This creates an ASR log file at %systemroot%\asr.log, // and also initialises Gbl_AsrLogFileHandle. // VOID AsrpInitialiseLogFile() { PWSTR currentSystemDevice = NULL; Gbl_AsrLogFileHandle = NULL; Gbl_AsrSystemVolumeHandle = NULL; // // Get full path to the error file. // Gbl_AsrLogFilePath = AsrpExpandEnvStrings(Asr_AsrLogFilePath); if (!Gbl_AsrLogFilePath) { goto OPENSYSTEMHANDLE; } // // Create an empty file (over-write it if it already exists). // Gbl_AsrLogFileHandle = CreateFileW( Gbl_AsrLogFilePath, // lpFileName GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess FILE_SHARE_READ, // dwShareMode: nobody else should write to the log file while we are NULL, // lpSecurityAttributes OPEN_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes: write through so we flush NULL // hTemplateFile ); if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) { // // Move to the end of file // SetFilePointer(Gbl_AsrLogFileHandle, 0L, NULL, FILE_END); } else { AsrpPrintDbgMsg(_asrlog, "Unable to create/open ASR log file at %ws (0x%x)\r\n", Gbl_AsrLogFilePath, GetLastError() ); } OPENSYSTEMHANDLE: // // Open a handle to the system volume. This is needed since the system // disk might otherwise be removed and added back by PnP during the // device detecion and re-installation phase (which will cause the // HKLM\System\Setup\SystemPartition key to be out-of-sync, and apps/ // components such as LDM that depend on that key to find the system // partition will fail). // // The more permanent work-around to this involves a change in mountmgr, // (such that it updates this key everytime the system volume disappears // and reappears) but for now, holding an open handle to the system // volume should suffice. // // See Windows Bugs 155675 for more information. // currentSystemDevice = AsrpExpandEnvStrings(Asr_SystemDeviceWin32Path); if (currentSystemDevice) { Gbl_AsrSystemVolumeHandle = CreateFileW( currentSystemDevice, // lpFileName FILE_READ_ATTRIBUTES, // dwDesiredAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode NULL, // lpSecurityAttributes OPEN_EXISTING, // dwCreationFlags FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes: write through so we flush NULL // hTemplateFile ); if ((Gbl_AsrSystemVolumeHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrSystemVolumeHandle)) { AsrpPrintDbgMsg(_asrinfo, "Opened a handle to the system volume %ws\r\n", currentSystemDevice); } else { AsrpPrintDbgMsg(_asrinfo, "Unable to open a handle to the system volume %ws (0x%x)\r\n", currentSystemDevice, GetLastError() ); } _AsrFree(currentSystemDevice); } else { AsrpPrintDbgMsg(_asrinfo, "Unable to get current system volume (0x%x)\r\n", GetLastError()); } } // // This creates an empty ASR error file at %systemroot%\asr.err, // and also initialises Gbl_AsrErrorFilePath with the full path // to asr.err // VOID AsrpInitialiseErrorFile() { HANDLE errorFileHandle = NULL; PWSTR lpOldFileName = NULL; DWORD size = 0; BOOL bResult = FALSE; char UnicodeFlag[3]; // // Get full path to the error file. // Gbl_AsrErrorFilePath = AsrpExpandEnvStrings(Asr_AsrErrorFilePath); if (!Gbl_AsrErrorFilePath) { return; } lpOldFileName = AsrpExpandEnvStrings(Asr_OldAsrErrorFilePath); if (lpOldFileName) { // // If the file already exists, move it to asr.err.old // MoveFileExW(Gbl_AsrErrorFilePath, lpOldFileName, MOVEFILE_REPLACE_EXISTING); } // // Create an empty file (append to it if it already exists), and close it // immediately // errorFileHandle = CreateFileW( Gbl_AsrErrorFilePath, // lpFileName GENERIC_WRITE, // dwDesiredAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode NULL, // lpSecurityAttributes CREATE_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((errorFileHandle) && (INVALID_HANDLE_VALUE != errorFileHandle)) { sprintf(UnicodeFlag, "%c%c", 0xFF, 0xFE); WriteFile(errorFileHandle, UnicodeFlag, strlen(UnicodeFlag)*sizeof(char), &size, NULL); CloseHandle(errorFileHandle); DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_TRACE_LEVEL, "ASR %c%lu Create ASR error file at %ws\r\n", THIS_MODULE, __LINE__, Gbl_AsrErrorFilePath); } else { DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "ASR %c%lu (ERROR) Unable to create ASR error file at %ws (0x%lu)\r\n", THIS_MODULE, __LINE__, Gbl_AsrErrorFilePath, GetLastError()); } } VOID AsrpCloseLogFiles() { if (Gbl_AsrErrorFilePath) { _AsrFree(Gbl_AsrErrorFilePath); } if (Gbl_AsrLogFilePath) { _AsrFree(Gbl_AsrLogFilePath); } if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) { CloseHandle(Gbl_AsrLogFileHandle); Gbl_AsrLogFileHandle = NULL; } } // // This executes "notepad ". If we encounter a critical // failure, we display the error log to the user, and reboot. We // document that any critical application that returns a fatal error // code is required to make an entry explaining the error in the // ASR error file. // VOID AsrpExecuteOnFatalError() { BOOL result = FALSE; DWORD exitCode = 0; PWSTR onFatalCmd = NULL; if (!Gbl_AsrErrorFilePath) { MYASSERT(0 && L"ExecuteOnFatalError called before InitialiseErrorFile: Gbl_ErrorFilePath is NULL"); return; } // // Make the error file read-only, so that the user's changes // aren't accidentally saved. // result = SetFileAttributesW(Gbl_AsrErrorFilePath, FILE_ATTRIBUTE_READONLY); if (!result) { AsrpPrintDbgMsg(_asrwarn, "Setup was unable to reset file attributes on file [%ws] to read-only (0x%x)\r\n", Gbl_AsrErrorFilePath, GetLastError() ); } // // Pop up the ASR failed wizard page. // // // Finally run "notepad " // onFatalCmd = AsrpExpandEnvStrings(Asr_FatalErrorCommand); if (!onFatalCmd) { // // Nothing we can do here--we couldn't find the command // to execute on fatal errors. Just bail--this is going // to make the system reboot. // return; } result = InvokeExternalApplication( NULL, // no Application Name onFatalCmd, // the full command string &exitCode // we want a synchronous execution ); if (!result) { SetFileAttributesW(Gbl_AsrErrorFilePath, FILE_ATTRIBUTE_NORMAL); AsrpPrintDbgMsg(_asrwarn, "Setup was unable to display error file, [%ws] failed (0x%x)\r\n", onFatalCmd, GetLastError() ); } _AsrFree(onFatalCmd); } BOOL AsrpSetFileSecurity( ) { DWORD dwStatus = ERROR_SUCCESS; SECURITY_ATTRIBUTES securityAttributes; SECURITY_DESCRIPTOR securityDescriptor; BOOL bResult = FALSE; if ((!Gbl_AsrErrorFilePath) || (!Gbl_AsrLogFilePath)) { SetLastError(ERROR_FILE_NOT_FOUND); AsrpPrintDbgMsg(_asrlog, "Unable to set backup operator permissions for log/error files (0x2)\r\n"); return FALSE; } securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); securityAttributes.lpSecurityDescriptor = &securityDescriptor; securityAttributes.bInheritHandle = FALSE; bResult = AsrpConstructSecurityAttributes(&securityAttributes, esatFile, TRUE); _AsrpErrExitCode((!bResult), dwStatus, GetLastError()); bResult = SetFileSecurity(Gbl_AsrErrorFilePath, DACL_SECURITY_INFORMATION, &securityDescriptor ); _AsrpErrExitCode((!bResult), dwStatus, GetLastError()); AsrpPrintDbgMsg(_asrinfo, "Set backup operator permissions for error file at %ws\r\n", Gbl_AsrErrorFilePath ); bResult = SetFileSecurity(Gbl_AsrLogFilePath, DACL_SECURITY_INFORMATION, &securityDescriptor ); _AsrpErrExitCode((!bResult), dwStatus, GetLastError()); AsrpPrintDbgMsg(_asrinfo, "Set backup operator permissions for log file at %ws\r\n", Gbl_AsrLogFilePath ); EXIT: AsrpCleanupSecurityAttributes(&securityAttributes); if (ERROR_SUCCESS != dwStatus) { SetLastError(dwStatus); } if (bResult) { AsrpPrintDbgMsg(_asrinfo, "Set backup operator permissions for files\r\n"); } else { AsrpPrintDbgMsg(_asrlog, "Unable to set backup operator permissions for log/error files (0x%lu)\r\n", GetLastError()); } return bResult; } /////////////////////////////////////////////////////////////////////////////// // Public function definitions. /////////////////////////////////////////////////////////////////////////////// VOID AsrInitialize(VOID) /*++ Description: Initializes the data structures required to complete ASR (Automated System Recovery, aka Disaster Recovery). This consists of reading the asr.sif file then initializing a list of recovery applications to be executed. Arguments: None. Returns: None. --*/ { PWSTR sifName = NULL; HINF sifHandle = NULL; BOOL result = FALSE; UINT errorLine = 0; SYSTEMTIME currentTime; GetSystemTime(¤tTime); // // Set the %TEMP% to c:\temp // AsrpSetEnvironmentVariables(); // // Initialise the log files // AsrpInitialiseErrorFile(); AsrpInitialiseLogFile(); AsrpPrintDbgMsg(_asrlog, "Entering GUI-mode Automated System Recovery. UTC: %04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu.\r\n", currentTime.wYear, currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond, currentTime.wMilliseconds ); // // Open the asr.sif file // sifName = AsrpExpandEnvStrings(AsrSifPath); if (!sifName) { AsrpPrintDbgMsg(_asrerror, "Setup was unable to locate the ASR state file asr.sif.\r\n"); FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0); } sifHandle = SetupOpenInfFileW( sifName, NULL, // Inf Class INF_STYLE_WIN4, &errorLine // Error-line ); if ((!sifHandle) || (INVALID_HANDLE_VALUE == sifHandle)) { AsrpPrintDbgMsg(_asrerror, "Setup was unable to open the ASR state file [%ws]. Error-code: 0x%x, Line %lu\r\n", sifName, GetLastError(), errorLine ); _AsrFree(sifName); FatalError(MSG_LOG_SYSINFBAD, L"asr.sif",0,0); } // // Add the "last instance" registry entry for ASR. // AsrpAddRegistryEntry(); // // Set the time-zone information. // result = AsrpRestoreTimeZoneInformation(sifName); if (!result) { AsrpPrintDbgMsg(_asrwarn, "Setup was unable to restore the time-zone information on the machine. (0x%x) ASR state file %ws\r\n", GetLastError(), (sifName ? sifName : L"could not be determined") ); } else { AsrpPrintDbgMsg(_asrlog, "Successfully restored time-zone information.\r\n"); } _AsrFree(sifName); //AsrpPerformSifIntegrityCheck(Handle); No check at the moment // // Make sure the licensed processors key is set. I'm adding this call here // since if this key isn't present when we reboot, the system bugchecks with // 9A: system_license_violation. // SetEnabledProcessorCount(); SetupCloseInfFile(sifHandle); Gbl_IsAsrEnabled = TRUE; } BOOL AsrIsEnabled(VOID) /*++ Description: Informs the caller whether ASR has been enabled by returning the value of the Gbl_IsAsrEnabled flag. Arguments: None. Returns: TRUE, if ASR is enabled. Otherwise, FALSE is returned. --*/ { return Gbl_IsAsrEnabled; } VOID AsrExecuteRecoveryApps(VOID) /*++ Description: Executes the commands in the [COMMANDS] section of the asr.sif file. Arguments: None. Returns: None. --*/ { BOOL errors = FALSE, result = FALSE; DWORD exitCode = 0; LONG criticalApp = 0; PWSTR sifPath = NULL; PWSTR application = NULL; PASR_RECOVERY_APP_NODE pNode = NULL; ASR_RECOVERY_APP_LIST list = {NULL, NULL, 0}; SYSTEMTIME currentTime; PWSTR errString = NULL; ASSERT_HEAP_IS_VALID(); // // Restore the non-critical disks. // SetLastError(ERROR_SUCCESS); sifPath = AsrpExpandEnvStrings(AsrSifPath); if (sifPath) { result = AsrpRestoreNonCriticalDisksW(sifPath, TRUE); } if (!result) { AsrpPrintDbgMsg(_asrwarn, "Setup was unable to restore the configuration of some of the disks on the machine. (0x%x) ASR state file %ws\r\n", GetLastError(), (sifPath ? sifPath : L"could not be determined") ); } else { AsrpPrintDbgMsg(_asrlog, "Successfully recreated disk configurations.\r\n"); } _AsrFree(sifPath); ASSERT_HEAP_IS_VALID(); // // Close the system handle // if ((Gbl_AsrSystemVolumeHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrSystemVolumeHandle)) { CloseHandle(Gbl_AsrSystemVolumeHandle); Gbl_AsrSystemVolumeHandle = NULL; AsrpPrintDbgMsg(_asrinfo, "Closed system device handle.\r\n"); } else { AsrpPrintDbgMsg(_asrinfo, "Did not have a valid system device handle to close.\r\n"); } // // Set the file security for the log and err files, to allow // backup operators to be able to access it on reboot // AsrpSetFileSecurity(); AsrpInitExecutionEnv(&list); // // Sort the list of recovery apps, by sequence number. // AsrpSortAppListBySequenceNumber(&list); // // Change the boot timeout value in the boot.ini file. We do this now, // since executed apps in the list might result in changing drive letter, // which would make finding boot.ini non-trivial. // if (!ChangeBootTimeout(30)) { AsrpPrintDbgMsg(_asrwarn, "Failed to change boot.ini timeout value.\r\n"); } // // Remove an application from the list and execute it. Continue until // no more applications remain. // pNode = AsrpRemoveFirstNodeFromList(&list); while (pNode && !errors) { application = AsrpBuildInvocationString(pNode); criticalApp = pNode->CriticalApp; // // We don't need pNode any more // if (pNode->RecoveryAppParams) { _AsrFree(pNode->RecoveryAppParams); } _AsrFree(pNode->RecoveryAppCommand); _AsrFree(pNode); // // if the cmd line couldn't be created: // for a critical app, fail. // for a non-critical app, move on to next // if (!application) { if (0 < criticalApp) { errors = TRUE; } } else { // // Launch the app // AsrpPrintDbgMsg(_asrlog, "Invoking external recovery application [%ws]\r\n", application); exitCode = ERROR_SUCCESS; SetLastError(ERROR_SUCCESS); result = InvokeExternalApplication( NULL, // no Application Name application, // the full command string &exitCode // we want a synchronous execution ); if (!result) { AsrpPrintDbgMsg(_asrerror, "Setup was unable to start the recovery application \"%ws\" (0x%x).\r\n", application, GetLastError() ); // // If a critical app couldn't be launched, it's a fatal error // if (0 < criticalApp) { errString = MyLoadString(IDS_ASR_ERROR_UNABLE_TO_LAUNCH_APP); if (errString) { swprintf(g_szErrorMessage, errString, application, GetLastError()); AsrpLogErrorMessage(g_szErrorMessage); MyFree(errString); errString = NULL; } else { FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); } errors = TRUE; } } else { // // Application was started: check the return code. If return // code is not zero and this is a critical app (ie criticalApp=1) // it is a fatal error // if ((ERROR_SUCCESS != exitCode) && (0 < criticalApp)) { 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); errString = MyLoadString(IDS_ASR_ERROR_RECOVERY_APP_FAILED); if (errString) { swprintf(g_szErrorMessage, errString, application, exitCode); AsrpLogErrorMessage(g_szErrorMessage); MyFree(errString); errString = NULL; } else { FatalError(MSG_LOG_OUTOFMEMORY, L"", 0, 0); } errors = TRUE; } else { AsrpPrintDbgMsg(_asrlog, "The recovery application \"%ws\" returned an exit code of 0x%x\r\n", application, exitCode); } } _AsrFree(application); } pNode = AsrpRemoveFirstNodeFromList(&list); } if (errors) { // // A critical app above did not return 0. // AsrpExecuteOnFatalError(); } else { // // We executed all the apps, without any critical failure. // RemoveRestartability(NULL); DeleteLocalSource(); AsrpMergeSetupLogIfNeeded(); AsrpPrintDbgMsg(_asrlog, "ASR completed successfully.\r\n"); } GetSystemTime(¤tTime); AsrpPrintDbgMsg(_asrlog, "Exiting from GUI-mode Automated System Recovery. UTC: %04hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu.\r\n", currentTime.wYear, currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond, currentTime.wMilliseconds ); // // Clean up global values // AsrpCloseLogFiles(); ASSERT_HEAP_IS_VALID(); }