/*++ Copyright (c) 1999 Microsoft Corporation Module Name: confdisk.cpp Abstract: Utility program to create an ASR state-file (asr.sif), or restore non-critical disk layout based on a previously created asr.sif. Author: Guhan Suriyanarayanan (guhans) 15-April-2001 Environment: User-mode only. Revision History: 15-Apr-2001 guhans Initial creation --*/ #include #include #include #include #include #include #include #include "resource.h" #include "critdrv.h" #include "confdisk.h" // // -------- // global variables used in this module // -------- // WCHAR g_szTempBuffer[BUFFER_LENGTH]; HMODULE g_hModule = NULL; HANDLE g_hHeap = NULL; BOOL g_fErrorMessageDone = FALSE; // // -------- // function implementations // -------- // VOID AsrpPrintError( IN CONST DWORD dwLineNumber, IN CONST DWORD dwErrorCode ) /*++ Routine Description: Loads an error message based on dwErrorCode from the resources, and prints it out to screen. There are some error codes that are of particular interest (that have specific error messages), others get a generic error message. Arguments: dwLineNumber - The line at which the error occured, pass in __LINE__ dwErrorCode - The win-32 error that occured. Return Value: None. --*/ { // // Handle the error codes we know and care about // switch (dwErrorCode) { case 0: break; default: // // Unexpected error, print out generic error message // LoadString(g_hModule, IDS_GENERIC_ERROR, g_szTempBuffer, BUFFER_LENGTH); wprintf(g_szTempBuffer, dwErrorCode, dwLineNumber); if ((ERROR_SUCCESS != dwErrorCode) && (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language g_szTempBuffer, BUFFER_LENGTH, NULL ))) { wprintf(L" %ws", g_szTempBuffer); } wprintf(L"\n"); } } PWSTR // must be freed by caller AsrpExpandEnvStrings( IN CONST PCWSTR lpOriginalString ) /*++ Routine Description: Allocates and returns a pointer to a new string containing a copy of the original string in which environment-variables replaced by their defined values. Uses the Win-32 API ExpandEnvironmentStrings. The caller must free the returned string using HeapFree(LocalProcessHeap). Arguments: lpOriginalString - Pointer to a null-terminated string that contains environment-variable strings of the form: %variableName%. For each such reference, the %variableName% portion is replaced with the current value of that environment variable. The replacement rules are the same as those used by the command interpreter. Case is ignored when looking up the environment- variable name. If the name is not found, the %variableName% portion is left undisturbed. Return Value: If the function succeeds, the return value is a pointer to the destination string containing the result of the expansion. The caller must free this memory using HeapFree for the current process heap. If the function fails, the return value is NULL. To get extended error information, call GetLastError(). --*/ { PWSTR lpszResult = NULL; UINT cchSize = MAX_PATH + 1, // start with a reasonable default cchRequiredSize = 0; BOOL bResult = FALSE; Alloc(lpszResult, PWSTR, cchSize * sizeof(WCHAR)); if (!lpszResult) { return NULL; } cchRequiredSize = ExpandEnvironmentStringsW(lpOriginalString, lpszResult, cchSize); if (cchRequiredSize > cchSize) { // // Buffer wasn't big enough; free and re-allocate as needed // Free(lpszResult); cchSize = cchRequiredSize + 1; Alloc(lpszResult, PWSTR, cchSize * sizeof(WCHAR)); if (!lpszResult) { return NULL; } cchRequiredSize = ExpandEnvironmentStringsW(lpOriginalString, lpszResult, cchSize); } if ((0 == cchRequiredSize) || (cchRequiredSize > cchSize)) { // // Either the function failed, or the buffer wasn't big enough // even on the second try // Free(lpszResult); // sets it to NULL } return lpszResult; } DWORD AsrpPrintUsage() /*++ Routine Description: Loads and prints the incorrect-usage error string. Arguments: None Return Values: None --*/ { wcscpy(g_szTempBuffer, L""); LoadString(g_hModule, IDS_ERROR_USAGE, g_szTempBuffer, BUFFER_LENGTH); wprintf(g_szTempBuffer, L"confdisk /save ", L"confdisk /restore", L"confdisk /save c:\\asr.sif"); return ERROR_INVALID_PARAMETER; } // // -------- // functions used by /save // -------- // BOOL AsrpAcquirePrivilege( IN CONST PCWSTR lpPrivilegeName ) /*++ Routine Description: Acquires the requested privilege (such as the backup privilege). Arguments: lpPrivilegeName - The required privilege (such as SE_BACKUP_NAME) Return Value: If the function succeeds, the return value is a nonzero value. If the function fails, the return value is zero. To get extended error information, call GetLastError(). --*/ { HANDLE hToken = NULL; BOOL bResult = FALSE; LUID luid; DWORD dwStatus = ERROR_SUCCESS; TOKEN_PRIVILEGES tNewState; bResult = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hToken); ErrExitCode(!bResult, dwStatus, GetLastError()); bResult = LookupPrivilegeValue(NULL, lpPrivilegeName, &luid); ErrExitCode(!bResult, dwStatus, GetLastError()); tNewState.PrivilegeCount = 1; tNewState.Privileges[0].Luid = luid; tNewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // We will always call GetLastError below, so clear // any prior error values on this thread. // SetLastError(ERROR_SUCCESS); bResult = AdjustTokenPrivileges( hToken, // Token Handle FALSE, // DisableAllPrivileges &tNewState, // NewState (DWORD) 0, // BufferLength NULL, // PreviousState NULL // ReturnLength ); // // Supposedly, AdjustTokenPriveleges always returns TRUE // (even when it fails). So, call GetLastError to be // extra sure everything's cool. // if (ERROR_SUCCESS != GetLastError()) { bResult = FALSE; } ErrExitCode(!bResult, dwStatus, GetLastError()); EXIT: _AsrpCloseHandle(hToken); SetLastError(dwStatus); return bResult; } DWORD AsrpCreateSif( IN CONST PCWSTR lpSifPath OPTIONAL ) /*++ Routine Description: Creates an ASR state file (asr.sif) at the requested location, using the ASR API from syssetup.dll. Arguments: lpSifPath - A null terminated UNICODE string containing the full path and file-name of the ASR state file to be created. This parameter may contain unexpanded environment variables between "%" signs (such as %systemroot%\repair\asr.sif), This parameter can be NULL. If it is NULL, the ASR state-file is created at the default location (%systemroot%\repair\asr.sif). Return Value: If the function succeeds, the return value is zero. If the function fails, the return value is a Win-32 error code. --*/ { HMODULE hDll = NULL; BOOL bResult = FALSE; DWORD_PTR asrContext = NULL; DWORD dwStatus = ERROR_SUCCESS; PWSTR lpProvider = NULL, lpCriticalVolumes = NULL; BOOL (*pfnCreateSif)(PCWSTR, PCWSTR, CONST BOOL, PCWSTR, DWORD_PTR*); BOOL (*pfnFreeContext)(DWORD_PTR* ); pfnCreateSif = NULL; pfnFreeContext = NULL; // // We need to acquire the backup privileges to create asr.sif // bResult = AsrpAcquirePrivilege(SE_BACKUP_NAME); ErrExitCode(!bResult, dwStatus, ERROR_PRIVILEGE_NOT_HELD); bResult = AsrpAcquirePrivilege(SE_RESTORE_NAME); ErrExitCode(!bResult, dwStatus, ERROR_PRIVILEGE_NOT_HELD); // // Get the critical volume list // lpCriticalVolumes = pFindCriticalVolumes(); ErrExitCode(!lpCriticalVolumes, dwStatus, ERROR_PRIVILEGE_NOT_HELD); // // Load syssetup, and find the routines to call // hDll = LoadLibraryW(L"syssetup.dll"); ErrExitCode(!hDll, dwStatus, GetLastError()); pfnCreateSif = (BOOL (*)(PCWSTR, PCWSTR, CONST BOOL, PCWSTR, DWORD_PTR*)) GetProcAddress(hDll, "AsrCreateStateFileW"); ErrExitCode(!pfnCreateSif, dwStatus, GetLastError()); pfnFreeContext = (BOOL (*)(DWORD_PTR *)) GetProcAddress(hDll, "AsrFreeContext"); ErrExitCode(!pfnFreeContext, dwStatus, GetLastError()); // // Finally, call the routine to create the state file: // bResult = pfnCreateSif(lpSifPath, // lpFilePath, lpProvider, // lpProviderName TRUE, // bEnableAutoExtend lpCriticalVolumes, // mszCriticalVolumes &asrContext // lpAsrContext ); ErrExitCode(!bResult, dwStatus, GetLastError()); EXIT: // // Cleanup // if (lpCriticalVolumes) { delete lpCriticalVolumes; lpCriticalVolumes = NULL; } if (pfnFreeContext && asrContext) { pfnFreeContext(&asrContext); } if (hDll) { FreeLibrary(hDll); hDll = NULL; } return dwStatus; } // // -------- // functions used by /restore // -------- // PWSTR AsrpReadField( PINFCONTEXT pInfContext, DWORD dwFieldIndex ) /*++ Routine Description: Reads and returns a pointer to string at the specified index from a sif. The caller must free the returned string using HeapFree(LocalProcessHeap). Arguments: pInfContext - The Inf Context to use to read the value, obtained from SetupGetLineByIndexW. dwFieldIndex - The 1 based field index of the string value to read. Return Value: If the function succeeds, the return value is a pointer to the destination string. The caller must free this memory using HeapFree for the current process heap. If the function fails, the return value is NULL. To get extended error information, call GetLastError(). --*/ { DWORD cchReqdSize = 0; BOOL bResult = FALSE; PWSTR lpszData = NULL; DWORD dwStatus = ERROR_SUCCESS; // // Allocate memory and read the data // Alloc(lpszData, PWSTR, MAX_PATH * sizeof(WCHAR)); ErrExitCode(!lpszData, dwStatus, GetLastError()); bResult = SetupGetStringFieldW(pInfContext, dwFieldIndex, lpszData, MAX_PATH, &cchReqdSize); if (!bResult) { dwStatus = GetLastError(); // // If our buffer was too small, allocate a larger buffer // and try again // if (ERROR_INSUFFICIENT_BUFFER == dwStatus) { dwStatus = ERROR_SUCCESS; Free(lpszData); Alloc(lpszData, PWSTR, (cchReqdSize * sizeof(WCHAR))); ErrExitCode(!lpszData, dwStatus, GetLastError()); bResult = SetupGetStringFieldW(pInfContext, dwFieldIndex, lpszData, cchReqdSize, NULL); } } if (!bResult) { Free(lpszData); } EXIT: return lpszData; } VOID AsrpInsertNodeToList( IN OUT PASR_RECOVERY_APP_LIST pList, IN OUT PASR_RECOVERY_APP_NODE pNode ) /*++ Routine Description: Does an insertion sort using the SequenceNumber as the key, to insert a Node to a List. Arguments: pList - The List in which to insert the node. pNode - The Node to insert. Return Value: None --*/ { PASR_RECOVERY_APP_NODE pPrev = NULL, pCurr = NULL; if (pList->AppCount == 0) { // // First node being added // pNode->Next = NULL; pList->First = pNode; } else { // // Find the slot to insert this in, based on the SequenceNumber // pCurr = pList->First; pPrev = NULL; while ((pCurr) && (pCurr->SequenceNumber < pNode->SequenceNumber)) { pPrev = pCurr; pCurr = pCurr->Next; } if (pPrev) { pPrev->Next = pNode; } else { pList->First = pNode; // Head of the list } pNode->Next = pCurr; } pList->AppCount += 1; } PASR_RECOVERY_APP_NODE AsrpGetNextRecoveryApp( IN OUT PASR_RECOVERY_APP_LIST pList ) /*++ Routine Description: Removes and returns the first Node in a List. Arguments: pList - The List from which to remove Return Value: A pointer to the first Node in the List. Note that this Node is removed from the list. NULL if the List is empty. --*/ { PASR_RECOVERY_APP_NODE pNode = NULL; if (pList->AppCount > 0) { pNode = pList->First; pList->First = pNode->Next; pList->AppCount -= 1; } return pNode; } DWORD AsrpBuildRecoveryAppList( IN CONST PCWSTR lpSifPath, OUT PASR_RECOVERY_APP_LIST pList ) /*++ Routine Description: Parses the COMMANDS section of asr.sif, and builds a list of recovery apps (that have SequenceNumber < 4000) to be launched. It skips apps with SequenceNumbers >= 4000 so that we don't launch the actual backup-and-restore (supposed to use sequence numbers >= 4000) listed. Arguments: lpSifPath - A null terminated UNICODE string containing the full path and file-name of the ASR state file to be used for the recovery. pList - Pointer to a struct that will receive the list recovery apps to be launched. Return Value: If the function succeeds, the return value is zero. If the function fails, the return value is a Win-32 error code. --*/ { INFCONTEXT inf; HINF hSif = NULL; LONG line = 0, lLineCount = 0; BOOL bResult = FALSE; INT iSequenceNumber = 0; DWORD dwStatus = ERROR_SUCCESS; PASR_RECOVERY_APP_NODE pNode = NULL; // // Open asr.sif and build the list of commands to be launched. // hSif = SetupOpenInfFileW(lpSifPath, NULL, INF_STYLE_WIN4, NULL); ErrExitCode((!hSif || (INVALID_HANDLE_VALUE == hSif)), dwStatus, GetLastError()); // // Read the COMMANDS section, and add each command to our list // lLineCount = SetupGetLineCountW(hSif, L"COMMANDS"); for (line = 0; line < lLineCount; line++) { // // Get the inf context for the line in asr.sif. This will be used // to read the fields on that line // bResult = SetupGetLineByIndexW(hSif, L"COMMANDS", line, &inf); ErrExitCode(!bResult, dwStatus, ERROR_INVALID_DATA); // // Read in the int fields. First, check the SequenceNumber, and skip // this record if the SequenceNumber is >= 4000 // bResult = SetupGetIntField(&inf, SequenceNumber, &iSequenceNumber); ErrExitCode(!bResult, dwStatus, ERROR_INVALID_DATA); if (iSequenceNumber >= 4000) { continue; } // // Create a new node // Alloc(pNode, PASR_RECOVERY_APP_NODE, sizeof(ASR_RECOVERY_APP_NODE)); ErrExitCode(!pNode, dwStatus, GetLastError()); pNode->SequenceNumber = iSequenceNumber; bResult = SetupGetIntField(&inf, SystemKey, &(pNode->SystemKey)); ErrExitCode(!bResult, dwStatus, ERROR_INVALID_DATA); bResult = SetupGetIntField(&inf, CriticalApp, &(pNode->CriticalApp)); ErrExitCode(!bResult, dwStatus, ERROR_INVALID_DATA); // // Read in the string fields // pNode->RecoveryAppCommand = AsrpReadField(&inf, CmdString); ErrExitCode((!pNode->RecoveryAppCommand), dwStatus, ERROR_INVALID_DATA); pNode->RecoveryAppParams = AsrpReadField(&inf, CmdParams); // null okay // // Add this node to our list, and move on to next // AsrpInsertNodeToList(pList, pNode); } EXIT: if (hSif && (INVALID_HANDLE_VALUE != hSif)) { SetupCloseInfFile(hSif); } return dwStatus; } PWSTR AsrpBuildInvocationString( IN PASR_RECOVERY_APP_NODE pNode, IN CONST PCWSTR lpSifPath ) /* Routine Description: Builds the invocation string, as the name suggests. It expands out the environment variables in the recovery app path, 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:\temp\app.exe /param1 /param2 /sifpath=c:\windows\repair\asr.sif Arguments: pNode - The node from which to build the invocation string. lpSifPath - A null terminated UNICODE string containing the full path and file-name of the ASR state file to be used for the recovery. This parameter may contain unexpanded environment variables between "%" signs (such as %systemroot%\repair\asr.sif), Return Value: If the function succeeds, the return value is a pointer to the destination string containing the result of the expansion. The caller must free this memory using HeapFree for the current process heap. If the function fails, the return value is NULL. To get extended error information, call GetLastError(). */ { PWSTR lpszApp = pNode->RecoveryAppCommand, lpszArgs = pNode->RecoveryAppParams, lpszCmd = NULL, lpszFullcmd = NULL; DWORD dwSize = 0; // // 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 // dwSize = sizeof(WCHAR) * ( wcslen(lpszApp) + // app name %TEMP%\ntbackup (lpszArgs ? wcslen(lpszArgs) : 0) + // arguments recover /1 wcslen(lpSifPath) + // path to sif c:\windows\repair\asr.sif 25 // spaces, null, "/sifpath=", etc ); Alloc(lpszCmd, PWSTR, dwSize); if (lpszCmd) { // // Build the string // swprintf(lpszCmd, L"%ws %ws /sifpath=%ws", lpszApp, (lpszArgs? lpszArgs: L""), lpSifPath); // // Expand the %% stuff, to build the full path // lpszFullcmd = AsrpExpandEnvStrings(lpszCmd); Free(lpszCmd); } return lpszFullcmd; } VOID AsrpSetEnvironmentVariables() /*++ Routine Description: Set some environment variables of interest. Arguments: None Return Value: None --*/ { PWSTR TempPath = AsrpExpandEnvStrings(L"%systemdrive%\\TEMP"); // // Set the TEMP and TMP variables to the same as GUI-mode recovery // SetEnvironmentVariableW(L"TEMP", TempPath); SetEnvironmentVariableW(L"TMP", TempPath); Free(TempPath); // // Clear this variable (it shouldn't exist anyway), since this is // meant to be set only if this is a full GUI-mode ASR // SetEnvironmentVariableW(L"ASR_C_CONTEXT", NULL); } DWORD AsrpRestoreSif( IN CONST PCWSTR lpSifPath ) /*++ Routine Description: Restores the disk layout specified in the ASR state file (asr.sif), using the ASR API in syssetup.dll. Then launches the recovery apps specified in the COMMANDS section of asr.sif, with sequence numbers less than 4000. Arguments: lpSifPath - A null terminated UNICODE string containing the full path and file-name of the ASR state file to be used for the recovery. This parameter may contain unexpanded environment variables between "%" signs (such as %systemroot%\repair\asr.sif), Return Value: If the function succeeds, the return value is zero. If the function fails, the return value is a Win-32 error code. --*/ { BOOL bResult = TRUE; HMODULE hDll = NULL; STARTUPINFOW startUpInfo; PWSTR lpFullSifPath = NULL, lpszAppCmdLine = NULL; ASR_RECOVERY_APP_LIST AppList; DWORD dwStatus = ERROR_SUCCESS; PROCESS_INFORMATION processInfo; PASR_RECOVERY_APP_NODE pNode = NULL; BOOL (*pfnRestoreDisks)(PCWSTR, BOOL); pfnRestoreDisks = NULL; ZeroMemory(&AppList, sizeof(ASR_RECOVERY_APP_LIST)); ZeroMemory(&startUpInfo, sizeof(STARTUPINFOW)); ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); lpFullSifPath = AsrpExpandEnvStrings(lpSifPath); ErrExitCode(!lpFullSifPath, dwStatus, GetLastError()); // // To restore the disks, load syssetup and get the routine of interest // hDll = LoadLibraryW(L"syssetup.dll"); ErrExitCode(!hDll, dwStatus, GetLastError()); pfnRestoreDisks = (BOOL (*)(PCWSTR, BOOL)) GetProcAddress(hDll, "AsrpRestoreNonCriticalDisksW"); ErrExitCode(!pfnRestoreDisks, dwStatus, GetLastError()); AsrpSetEnvironmentVariables(); // // Recreate the disks. We don't need the AllOrNothing granularity--it's // okay if some disks come back but others don't, // bResult = pfnRestoreDisks(lpFullSifPath, FALSE); ErrExitCode(!bResult, dwStatus, GetLastError()); // // Now, we need to launch the recovery apps in the COMMANDS section. // Note that we'll only launch apps with a sequence number below 4000, // so that we don't launch the actual backup-and-restore app listed. // Backup-and-restore apps are supposed to use sequence numbers >= 4000. // // // Parse the sif to obtain list of apps to run // dwStatus = AsrpBuildRecoveryAppList(lpFullSifPath, &AppList); ErrExitCode((ERROR_SUCCESS != dwStatus), dwStatus, dwStatus); // // And launch them synchronously. // pNode = AsrpGetNextRecoveryApp(&AppList); while (pNode) { lpszAppCmdLine = AsrpBuildInvocationString(pNode, lpFullSifPath); // // We don't need pNode any more // Free(pNode->RecoveryAppParams); Free(pNode->RecoveryAppCommand); Free(pNode); if (!lpszAppCmdLine) { // // Silently fail !TODO: May need error message // continue; } // !TODO: May need status message wprintf(L"[%ws]\n", lpszAppCmdLine); bResult = CreateProcessW( NULL, // lpApplicationName lpszAppCmdLine, // lpCommandLine NULL, // lpProcessAttributes NULL, // lpThreadAttributes FALSE, // bInheritHandles 0, // dwCreationFlags NULL, // pEnvironment NULL, // lpCurrentDirectory (null=current dir) &startUpInfo, // statup information &processInfo // process information ); if (bResult) { WaitForSingleObject(processInfo.hProcess, INFINITE); } // else silently fail !TODO: May need error message Free(lpszAppCmdLine); pNode = AsrpGetNextRecoveryApp(&AppList); } EXIT: if (hDll && (INVALID_HANDLE_VALUE != hDll)) { FreeLibrary(hDll); hDll = NULL; } return dwStatus; } int __cdecl wmain( int argc, WCHAR *argv[], WCHAR *envp[] ) /*++ Routine Description: Entry point to the application. Arguments: argc - Number of command-line parameters used to invoke the app argv - The command-line parameters as an array of strings. See the top of this module for the list of valid parameters. envp - The process environment block, not currently used Return Values: If the function succeeds, the exit code is zero. If the function fails, the exit code is a win-32 error code. --*/ { DWORD dwStatus = ERROR_SUCCESS; // // Initialise globals // g_hModule = GetModuleHandle(NULL); g_hHeap = GetProcessHeap(); g_fErrorMessageDone = FALSE; // // Check and switch on the basis of the command line arguments // if ((argc >= 2) && ( !_wcsicmp(argv[1], L"/save") || !_wcsicmp(argv[1], L"-save") || !_wcsicmp(argv[1], L"save") )) { // // confdisk /save [c:\windows\asr.sif] // dwStatus = AsrpCreateSif(argv[2]); } else if ((argc >= 3) && ( !_wcsicmp(argv[1], L"/restore") || !_wcsicmp(argv[1], L"-restore") || !_wcsicmp(argv[1], L"restore") )) { // // confdisk /restore c:\windows\repair\asr.sif // dwStatus = AsrpRestoreSif(argv[2]); } else { // // Unknown parameter // dwStatus = AsrpPrintUsage(); } // // We're all done. Return the error-code, for interested parties. // return (int) dwStatus; UNREFERENCED_PARAMETER(envp); }