/*++ Copyright (c) 1999 Microsoft Corporation Module Name: asr_dlg.cpp Abstract: Top level code to backup and restore information about the volumes on a system. This module handles the main application dialogue, including parsing the command-line, and updating the progress bar and UI status text. Authors: Steve DeVos (Veritas) (v-stevde) 15-May-1998 Guhan Suriyanarayanan (guhans) 21-Aug-1999 Environment: User-mode only. Revision History: 15-May-1998 v-stevde Initial creation 21-Aug-1999 guhans Cleaned up and re-wrote this module. --*/ #include "stdafx.h" #include "asr_fmt.h" #include "asr_dlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif BOOLEAN g_bQuickFormat = FALSE; ///////////////////////////////////////////////////////////////////////////// // CAsr_fmtDlg dialog CAsr_fmtDlg::CAsr_fmtDlg(CWnd* pParent /*=NULL*/) : CDialog(CAsr_fmtDlg::IDD, pParent) { //{{AFX_DATA_INIT(CAsr_fmtDlg) //}}AFX_DATA_INIT } void CAsr_fmtDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAsr_fmtDlg) DDX_Control(pDX, IDC_PROGRESS, m_Progress); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAsr_fmtDlg, CDialog) //{{AFX_MSG_MAP(CAsr_fmtDlg) //}}AFX_MSG_MAP // manually added message handlers (for user-defined messages) should be added OUTSIDE // the AFX_MSG_MAP part above ON_MESSAGE(WM_WORKER_THREAD_DONE, OnWorkerThreadDone) ON_MESSAGE(WM_UPDATE_STATUS_TEXT, OnUpdateStatusText) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAsr_fmtDlg message handlers // ON_BN_CLICKED(IDOK, OnWorkerThreadDone) BOOL CAsr_fmtDlg::OnInitDialog() { CDialog::OnInitDialog(); // initialize the progress range and start posiiton m_Progress.SetRange(0, 100); m_Progress.SetPos(0); m_ProgressPosition = 0; // Launch the worker thread. CreateThread(NULL, // no sid 0, // no initial stack size (LPTHREAD_START_ROUTINE) CAsr_fmtDlg::DoWork, this, // parameter 0, // no flags NULL // no thread ID ); return TRUE; // return TRUE unless you set the focus to a control } LRESULT CAsr_fmtDlg::OnWorkerThreadDone( WPARAM wparam, LPARAM lparam ) { EndDialog(m_dwEndStatus); return 0; } LRESULT CAsr_fmtDlg::OnUpdateStatusText( WPARAM wparam, LPARAM lparam ) { SetDlgItemText(IDC_STATUS_TEXT, m_strStatusText); m_Progress.SetPos(m_ProgressPosition); return 0; } /** Name: DoWork() Description: This function runs as a thread launched form the Init of the dialog. This function determines what needs to be done, then calls the appropraite function to do the work. Modified: 8/31/1998 Returns: TRUE Notes: If an error occurs EndDialog will be called with the exit status. Declaration: **/ long CAsr_fmtDlg::DoWork( CAsr_fmtDlg *_this ) { ASRFMT_CMD_OPTION cmdOption = cmdUndefined; cmdOption = _this->ParseCommandLine(); ((CAsr_fmtDlg *)_this)->m_AsrState = NULL; _this->m_dwEndStatus = ERROR_SUCCESS; switch (cmdOption) { case cmdBackup: { if (!(_this->BackupState())) { _this->m_dwEndStatus = GetLastError(); } break; } case cmdRestore: { if (!(_this->RestoreState())) { _this->m_dwEndStatus = GetLastError(); } break; } case cmdDisplayHelp: { _this->m_dwEndStatus = ERROR_INVALID_FUNCTION; // display help ... break; } } if (ERROR_INVALID_FUNCTION != _this->m_dwEndStatus) { // display help ... _this->PostMessage(WM_WORKER_THREAD_DONE, 0, 0); } return 0; } /** Name: BackupState() Description: This function reads the current state file to get the current data for the sections to modify. It then updates [VOLUMES], [REMOVABLEMEDIA] and [COMMANDS] sections. Notes: If an error occurs an Error message popup is provided to the user. Declaration: **/ BOOL CAsr_fmtDlg::BackupState() { BOOL result = FALSE; HANDLE hHeap = GetProcessHeap(); int i = 0; CString strPleaseWait; strPleaseWait.LoadString(IDS_PLEASE_WAIT_BACKUP); SetDlgItemText(IDC_PROGRESS_TEXT, strPleaseWait); m_AsrState = (PASRFMT_STATE_INFO) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, sizeof (ASRFMT_STATE_INFO) ); // // The only purpose of the for loops below are to slow down the // UI and make the progress bar proceed smoothly, so that the user // can read the dialogue box on screen. // m_strStatusText.LoadString(IDS_QUERY_SYS_FOR_INFO); for (i = 3; i < 15; i++) { m_ProgressPosition = i; PostMessage(WM_UPDATE_STATUS_TEXT); Sleep(50); } if (m_AsrState) { result = BuildStateInfo(m_AsrState); } if (!result) { goto EXIT; } // // The only purpose of the for loops below are to slow down the // UI and make the progress bar proceed smoothly, so that the user // can read the dialogue box on screen. // m_strStatusText.LoadString(IDS_QUERY_SYS_FOR_INFO); for (i = 15; i < 45; i++) { m_ProgressPosition = i; PostMessage(WM_UPDATE_STATUS_TEXT); Sleep(50); } // // Pretend we're doing something else (change UI text) // m_strStatusText.LoadString(IDS_BUILDING_VOL_LIST); for (i = 45; i < 80; i++) { m_ProgressPosition = i; PostMessage(WM_UPDATE_STATUS_TEXT); Sleep(50); } m_strStatusText.LoadString(IDS_WRITING_TO_SIF); for (i = 80; i < 90; i++) { m_ProgressPosition = i; PostMessage(WM_UPDATE_STATUS_TEXT); Sleep(50); } result = WriteStateInfo(m_dwpAsrContext, m_AsrState); if (!result) { goto EXIT; } m_strStatusText.LoadString(IDS_WRITING_TO_SIF); for (i = 90; i < 101; i++) { m_ProgressPosition = i; PostMessage(WM_UPDATE_STATUS_TEXT); Sleep(50); } EXIT: FreeStateInfo(&m_AsrState); return result; } /** Name: RestoreState() Description: This function restores the state found in the [VOLUMES] section of the asr.sif file. The state restored includs: the drive letter. If drive is inaccessible it is formated. the volume label. Notes: If an error occurs an Error message popup is provided to the user. **/ BOOL CAsr_fmtDlg::RestoreState() { BOOL bErrorsEncountered = FALSE, result = TRUE; CString strPleaseWait; strPleaseWait.LoadString(IDS_PLEASE_WAIT_RESTORE); SetDlgItemText(IDC_PROGRESS_TEXT, strPleaseWait); m_ProgressPosition = 0; m_strStatusText.LoadString(IDS_READING_SIF); PostMessage(WM_UPDATE_STATUS_TEXT); AsrfmtpInitialiseErrorFile(); // in case we need to log error messages // // 1. Read the state file // result = ReadStateInfo( (LPCTSTR)m_strSifPath, &m_AsrState); if (!result || !m_AsrState) { DWORD status = GetLastError(); CString strErrorTitle; CString strErrorMessage; CString strErrorFormat; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorFormat.LoadString(IDS_ERROR_NO_DRSTATE); strErrorMessage.Format(strErrorFormat, (LPCTSTR)m_strSifPath, status); AsrfmtpLogErrorMessage(_SeverityWarning, (LPCTSTR)strErrorMessage); AsrfmtpCloseErrorFile(); return FALSE; } // // 2. Loop through all the volumes listed in the state file. // PASRFMT_VOLUME_INFO pVolume = m_AsrState->pVolume; PASRFMT_REMOVABLE_MEDIA_INFO pMedia = m_AsrState->pRemovableMedia; UINT driveType = DRIVE_UNKNOWN; PWSTR lpDrive = NULL; // Display string, of the form \DosDevices\C: or \??\Volume{Guid} DWORD cchVolumeGuid = 0; WCHAR szVolumeGuid[MAX_PATH + 1]; int sizeIncrement = 0, i = 0; BOOL first = TRUE, isIntact = FALSE, isLabelIntact = TRUE; // // We need to first set all the guids a volume has, then try setting the drive // letters. Also, we need to go through the guid loop twice, to handle this case: // // Consider a sif where we had the entries // ... \vol1, \vol2 // ... \vol1, \x: // ... \vol1, \vol3 // // where vol1, vol2 and vol3 are volume guids (\??\Volume{Guid}), // and x: is the drive letter (\DosDevices\X:) // // Now, our list will contain three nodes: // -->(vol1, vol3)-->(vol1, x:)-->(vol1, vol2)-->/ // // The problem is, the partition could currently have the guid vol2. (since // it is free to have any one of the three guids). // // If we only went through the loop once, we'll try to map vol1 to vol3, or // vice-versa if vol1 doesn't exist. Since neither exist yet, we would've // complained to the user. // // The advantage to doing it twice is this: the first time, since neither vol1 // nor vol3 exist yet, we'll skip that node. Then we'll skip the drive letter for // now (since we're only doing guids), and eventually we'll map vol2 to vol1. // // The second time, we'll find vol1 exists (mapped to vol2), and we'll be // able to map vol1 to vol3. In a later loop, vol1 will also get the drive // letter X, and all's well. // // By the way, we could optimise this by creating a better data structure, // but it's too late in the game now for that. If the performance is // really bad, we could come back to this. // if (m_AsrState->countVolume > 1) { sizeIncrement = 50 / (m_AsrState->countVolume - 1); } else { sizeIncrement = 100; } m_ProgressPosition = 0; for (i = 0; i < 2; i++) { pVolume = m_AsrState->pVolume; while (pVolume) { m_ProgressPosition += sizeIncrement; m_strStatusText.Format(IDS_REST_SYM_LINKS, pVolume->szGuid); PostMessage(WM_UPDATE_STATUS_TEXT); if ((!pVolume->IsDosPathAssigned) && ASRFMT_LOOKS_LIKE_VOLUME_GUID(pVolume->szDosPath, (wcslen(pVolume->szDosPath) * sizeof(WCHAR))) ) { pVolume->IsDosPathAssigned = SetDosName(pVolume->szGuid, pVolume->szDosPath); if (!pVolume->IsDosPathAssigned) { pVolume->IsDosPathAssigned = SetDosName(pVolume->szDosPath, pVolume->szGuid); } } pVolume = pVolume->pNext; } } pVolume = m_AsrState->pVolume; FormatInitialise(); while (pVolume) { lpDrive = ((wcslen(pVolume->szDosPath) > 0) ? pVolume->szDosPath : pVolume->szGuid); // // Check if the drive is accessible. If not, we log an error message // and continue. GetDriveType requires the name in the DOS name space, so we // convert our GUID (NT name space) to the required format first // cchVolumeGuid = wcslen(pVolume->szGuid); if (cchVolumeGuid >= MAX_PATH) { cchVolumeGuid = MAX_PATH - 1; } wcsncpy(szVolumeGuid, pVolume->szGuid, cchVolumeGuid); szVolumeGuid[1] = L'\\'; szVolumeGuid[cchVolumeGuid] = L'\\'; // Trailing back-slash szVolumeGuid[cchVolumeGuid+1] = L'\0'; driveType = GetDriveType(szVolumeGuid); if (DRIVE_NO_ROOT_DIR == driveType) { CString strErrorTitle; CString strErrorMessage; CString strErrorFormat; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorFormat.LoadString(IDS_ERROR_MISSING_VOL); strErrorMessage.Format(strErrorFormat, (PWSTR) pVolume->szGuid); AsrfmtpLogErrorMessage(_SeverityWarning, (LPCTSTR)strErrorMessage); bErrorsEncountered = TRUE; } if (DRIVE_FIXED != driveType) { // // Strange. The volumes section should have only listed the drives that // were fixed (At backup time). This could probably mean that a volume that // used to be on a fixed drive is now on a removable drive?! // pVolume = pVolume->pNext; continue; } // // Set the drive letter of the volume // result = TRUE; if (!pVolume->IsDosPathAssigned){ m_ProgressPosition = 0; m_strStatusText.Format(IDS_REST_DRIVE_LETTER, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); result = SetDosName(pVolume->szGuid, pVolume->szDosPath); } if (!result) { CString strErrorTitle; CString strErrorMessage; CString strErrorFormat; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorFormat.LoadString(IDS_ERROR_MOUNTING); strErrorMessage.Format(strErrorFormat, (PWSTR) pVolume->szDosPath, (PWSTR) pVolume->szGuid); AsrfmtpLogErrorMessage(_SeverityWarning, (LPCTSTR)strErrorMessage); bErrorsEncountered = TRUE; pVolume = pVolume->pNext; continue; } // // Check if the volume needs to be formatted // m_ProgressPosition = 0; m_strStatusText.Format(IDS_CHECKING_VOLUME, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); isIntact = IsFsTypeOkay(pVolume, &isLabelIntact); if (isIntact) { isIntact = IsVolumeIntact(pVolume); } // // Format the volume if needed // if (!isIntact && FormatVolume(pVolume)) { isLabelIntact = FALSE; m_ProgressPosition = 0; m_strStatusText.Format(IDS_FORMATTING_VOLUME, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); // // FormatVolume is asynchronous. // first = TRUE; while (g_bFormatInProgress) { if (g_iFormatPercentComplete >= 100) { if (first) { m_ProgressPosition = 100; m_strStatusText.Format(IDS_CREATING_FS_STRUCT, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); first = FALSE; } } else { m_Progress.SetPos(g_iFormatPercentComplete); } Sleep(100); } if (!g_bFormatSuccessful) { CString strErrorTitle; CString strErrorMessage; CString strErrorFormat; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorFormat.LoadString(IDS_ERROR_FORMATTING); strErrorMessage.Format(strErrorFormat, (PWSTR) lpDrive); AsrfmtpLogErrorMessage(_SeverityWarning, (LPCTSTR)strErrorMessage); bErrorsEncountered = TRUE; pVolume = pVolume->pNext; continue; } // // Force the file-system to be mounted // MountFileSystem(pVolume); } // // Set the volume label if it isn't intact // if (!isLabelIntact) { m_ProgressPosition = 0; m_strStatusText.Format(IDS_REST_VOL_LABEL, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); if ((wcslen(pVolume->szFsName) > 0) && !SetVolumeLabel(szVolumeGuid, pVolume->szLabel)) { bErrorsEncountered = TRUE; pVolume = pVolume->pNext; continue; } } pVolume = pVolume->pNext; } FormatCleanup(); while (pMedia) { lpDrive = ((wcslen(pMedia->szDosPath) > 0) ? pMedia->szDosPath : pMedia->szVolumeGuid); // // set the drive letter // m_ProgressPosition = 0; m_strStatusText.Format(IDS_REST_DRIVE_LETTER, lpDrive); PostMessage(WM_UPDATE_STATUS_TEXT); result = SetRemovableMediaGuid(pMedia->szDevicePath, pMedia->szVolumeGuid); if (result) { result = SetDosName(pMedia->szVolumeGuid, pMedia->szDosPath); } if (!result) { CString strErrorTitle; CString strErrorMessage; CString strErrorFormat; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorFormat.LoadString(IDS_ERROR_MOUNTING); strErrorMessage.Format(IDS_ERROR_MOUNTING, (PWSTR) pMedia->szDosPath, (PWSTR) pMedia->szVolumeGuid); AsrfmtpLogErrorMessage(_SeverityWarning, (LPCTSTR)strErrorMessage); // ignore failures setting drive letter on CD. // bErrorsEncountered = TRUE; } pMedia = pMedia->pNext; } AsrfmtpCloseErrorFile(); return (bErrorsEncountered ? FALSE : TRUE); } inline BOOL IsGuiModeAsr() { WCHAR szAsrFlag[20]; // // If (and only if) this is GUI-mode ASR, the ASR_C_CONTEXT // environment variable is set to "AsrInProgress" // return (GetEnvironmentVariable(L"ASR_C_CONTEXT", szAsrFlag, 20) && !wcscmp(szAsrFlag, L"AsrInProgress")); } /** Name: ParseCommandLine() Description: This function reads the comand line and looks for the "backup" or "restore" options. Modified: 8/31/1998 Returns: cmdBackup, cmdRestore, or cmdDisplayHelp. Notes: If neither backup or restore are found, then the usage is displayed in the error box. Declaration: **/ ASRFMT_CMD_OPTION CAsr_fmtDlg::ParseCommandLine() { CString cmd; m_dwpAsrContext = 0; cmd = GetCommandLine(); cmd.MakeLower(); if (cmd.Find(TEXT("/backup")) != -1) { int pos = cmd.Find(TEXT("/context=")); if (pos > -1) { #ifdef _IA64_ _stscanf(cmd.Mid(pos, cmd.GetLength() - pos + 1), TEXT("/context=%I64u"), &m_dwpAsrContext); #else _stscanf(cmd.Mid(pos, cmd.GetLength() - pos + 1), TEXT("/context=%lu"), &m_dwpAsrContext); #endif return cmdBackup; } } else if (cmd.Find(TEXT("/restore")) != -1) { if (cmd.Find(TEXT("/full")) != -1) { g_bQuickFormat = FALSE; } else if (cmd.Find(TEXT("/quick")) != -1) { g_bQuickFormat = TRUE; } else if (IsGuiModeAsr()) { g_bQuickFormat = FALSE; } else { g_bQuickFormat = TRUE; } int pos = cmd.Find(TEXT("/sifpath=")); if (pos > -1) { _stscanf(cmd.Mid(pos, cmd.GetLength() - pos + 1), TEXT("/sifpath=%ws"), m_strSifPath.GetBuffer(1024)); m_strSifPath.ReleaseBuffer(); return cmdRestore; } } CString strErrorTitle; CString strErrorMessage; INT space_offset; strErrorTitle.LoadString(IDS_ERROR_TITLE); strErrorMessage.LoadString(IDS_ERROR_USAGE); space_offset = cmd.Find(' '); if (space_offset >0) { cmd = cmd.Left(cmd.Find(' ')); } SetDlgItemText(IDC_PROGRESS_TEXT, strErrorMessage); CWnd *pText = GetDlgItem(IDC_STATUS_TEXT); pText->ShowWindow(SW_HIDE); CButton *pButton = (CButton*)GetDlgItem(IDOK); pButton->ShowWindow(SW_SHOW); pButton->SetState(TRUE); pButton->SetCheck(TRUE); CWnd *pBar = GetDlgItem(IDC_PROGRESS); pBar->ShowWindow(SW_HIDE); return cmdDisplayHelp; }