/*++ Copyright (c) 1999 Microsoft Corporation Module Name: ftasr.cpp Abstract: Implementation of class CFtasrDlg This class provides a GUI interface for the FTASR process. It also implements the 3 main FTASR operations: "register" - register FTASR as a BackupRestore command "backup" - save the current FT hierarchy state in dr_state.sif file "restore" - restore the FT hierarchy as it is in dr_state.sif file Author: Norbert Kusters Cristian Teodorescu 3-March-1999 Notes: Revision History: --*/ #include "stdafx.h" #include "ftasr.h" #include "ftasrdlg.h" #include #include #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ULONG DbgPrint(PWSTR Format, ...) { WCHAR achMsg[256]; va_list vlArgs; va_start(vlArgs, Format); _vsnwprintf(achMsg, sizeof(achMsg), Format, vlArgs); va_end(vlArgs); OutputDebugString(achMsg); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CFtasrDlg dialog CFtasrDlg::CFtasrDlg( CWnd* pParent /*=NULL*/ ) : CDialog(CFtasrDlg::IDD, pParent) { //{{AFX_DATA_INIT(CFtasrDlg) //}}AFX_DATA_INIT m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CFtasrDlg::DoDataExchange( CDataExchange* pDX ) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CFtasrDlg) DDX_Control(pDX, IDC_PROGRESS, m_Progress); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CFtasrDlg, CDialog) //{{AFX_MSG_MAP(CFtasrDlg) ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}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() ///////////////////////////////////////////////////////////////////////////// // CFtasrDlg message handlers BOOL CFtasrDlg::OnInitDialog() { CDialog::OnInitDialog(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // initialize the progress range and start posiiton m_Progress.SetRange(0, 100 ); m_Progress.SetPos( 0 ) ; DWORD dummy ; // Launch the worker thread. CreateThread(NULL,0, (LPTHREAD_START_ROUTINE) CFtasrDlg::DoWork,this,0,&dummy ) ; return TRUE; // return TRUE unless you set the focus to a control } LRESULT CFtasrDlg::OnWorkerThreadDone( WPARAM wparam, LPARAM lparam ) { EndDialog( m_EndStatusCode ) ; return 0 ; } LRESULT CFtasrDlg::OnUpdateStatusText( WPARAM wparam, LPARAM lparam ) { SetDlgItemText( IDC_PROGRESS_TEXT, m_StatusText ) ; return 0 ; } long CFtasrDlg::DoWork( CFtasrDlg *_this ) { UINT operation ; operation = _this->ParseCommandLine(); //((CFtasrDlg *)_this)->m_drState = NULL ; _this->m_EndStatusCode = 0 ; switch( operation ) { case DISPLAY_HELP : _this->m_EndStatusCode = 2; break; case BACKUP_STATE: _this->m_EndStatusCode = _this->DoBackup(); break ; case RESTORE_STATE : _this->m_EndStatusCode = _this->DoRestore(); break ; case REGISTER_STATE : _this->m_EndStatusCode = _this->DoRegister(); break ; } _this->PostMessage( WM_WORKER_THREAD_DONE, 0, 0 ) ; return 0 ; } long CFtasrDlg::DoBackup() { BOOL b; DWORD numDisks, i; PFT_LOGICAL_DISK_ID diskId; FILE* output; long r; CHAR* windir; CHAR fileName[MAX_PATH]; m_StatusText.LoadString( IDS_BACKUP_STATE ); PostMessage( WM_UPDATE_STATUS_TEXT ); b = FtEnumerateLogicalDisks(0, NULL, &numDisks); if (!b) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } m_Progress.SetStep(100/(numDisks+3)); diskId = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numDisks*sizeof(FT_LOGICAL_DISK_ID)); if (!diskId) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } b = FtEnumerateLogicalDisks(numDisks, diskId, &numDisks); if (!b) { r = GetLastError(); LocalFree(diskId); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } #ifdef CHECK_UNSUPPORTED_CONFIG r = CheckForUnsupportedConfig(numDisks, diskId); if (r != ERROR_SUCCESS) { LocalFree(diskId); // The error message was already displayed in CheckForUnsupportedConfig return r; } #endif m_Progress.StepIt(); Sleep(300); windir = getenv("windir"); strcpy(fileName, windir); strcat(fileName, "\\repair\\dr_state.sif"); output = fopen(fileName, "r+"); if (!output) { LocalFree(diskId); DisplaySystemError(ERROR_FILE_NOT_FOUND); return ERROR_FILE_NOT_FOUND; } m_Progress.StepIt(); Sleep(300); r = AddToCommands(output); if (r != ERROR_SUCCESS) { fclose(output); LocalFree(diskId); DisplayResourceError(IDS_ERROR_ADD_TO_COMMANDS); return r; } if (fseek(output, 0, SEEK_END)) { fclose(output); LocalFree(diskId); DisplaySystemError(ERROR_INVALID_FUNCTION); return ERROR_INVALID_FUNCTION; } fprintf(output, "\n[FT.VolumeState]\n"); fprintf(output, "%lu\n", numDisks); m_Progress.StepIt(); Sleep(300); for (i = 0; i < numDisks; i++) { r = PrintOutVolumeNames(output, diskId[i]); if (r != ERROR_SUCCESS) { fclose(output); LocalFree(diskId); // The error message was already displayed in PrintOutVolumeNames return r; } r = PrintOutDiskInfo(output, diskId[i]); if (r != ERROR_SUCCESS) { fclose(output); LocalFree(diskId); // The error message was already displayed in PrintOutDiskInfo return r; } m_Progress.StepIt(); Sleep(300); } fclose(output); LocalFree(diskId); m_Progress.SetPos(101); return ERROR_SUCCESS; } long CFtasrDlg::DoRestore() { FILE* input; CHAR* windir; CHAR fileName[MAX_PATH]; CHAR string[MAX_PATH]; int result; m_StatusText.LoadString( IDS_RESTORE_STATE ); PostMessage( WM_UPDATE_STATUS_TEXT ); windir = getenv("windir"); strcpy(fileName, windir); strcat(fileName, "\\repair\\dr_state.sif"); input = fopen(fileName, "r"); if (!input) { DisplaySystemError(ERROR_FILE_NOT_FOUND); return ERROR_FILE_NOT_FOUND; } for (;;) { if (!fgets(string, MAX_PATH, input)) { fclose(input); DbgPrint(L"ftasr: Section [FT.VolumeState] not found\n"); m_Progress.SetPos(101); Sleep(300); return ERROR_SUCCESS; } if (!strncmp(string, "[FT.VolumeState]", 16)) { break; } } result = Restore(input); fclose(input); return result; } long CFtasrDlg::DoRegister() { int r; HKEY key; m_StatusText.LoadString( IDS_REGISTER_STATE ) ; PostMessage( WM_UPDATE_STATUS_TEXT ) ; r = RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\BackupRestore\\AsrCommands", 0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL); if (r != ERROR_SUCCESS) { DisplaySystemError(r); return r; } m_Progress.SetPos(50); Sleep(300) ; r = RegSetValueEx(key, L"FT Volumes Recovery", 0, REG_SZ, (CONST BYTE*) L"ftasr backup", 26); if (r != ERROR_SUCCESS) { DisplaySystemError(r); return r; } m_Progress.SetPos(101); Sleep(300) ; return r; } UINT CFtasrDlg::ParseCommandLine() { CString cmd; cmd = GetCommandLine() ; cmd.MakeLower( ) ; if (cmd.Find(TEXT("backup") ) != -1) { return BACKUP_STATE ; } else if (cmd.Find(TEXT("restore")) != -1) { return RESTORE_STATE ; } else if (cmd.Find(TEXT("register")) != -1) { return REGISTER_STATE; } CString error_msg ; error_msg.LoadString(IDS_ERROR_USAGE); DisplayError(error_msg); return DISPLAY_HELP; } void CFtasrDlg::DisplayError( const CString& ErrorMsg ) { CString title; title.LoadString(IDS_ERROR_TITLE); ::MessageBox(m_hWnd, ErrorMsg, title, MB_OK); } void CFtasrDlg::DisplaySystemError( DWORD ErrorCode ) { CString title; WCHAR messageBuffer[MAX_PATH]; title.LoadString(IDS_ERROR_TITLE); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, 0, messageBuffer, MAX_PATH, NULL); ::MessageBox(m_hWnd, messageBuffer, title, MB_OK); } void CFtasrDlg::DisplayResourceError( DWORD ErrorId ) { CString title, errorMsg; title.LoadString(IDS_ERROR_TITLE); errorMsg.LoadString(ErrorId); ::MessageBox(m_hWnd, errorMsg, title, MB_OK); } void CFtasrDlg::DisplayResourceSystemError( DWORD ErrorId, DWORD ErrorCode ) { CString title, errorMsg; WCHAR messageBuffer[MAX_PATH]; title.LoadString(IDS_ERROR_TITLE); errorMsg.LoadString(ErrorId); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, 0, messageBuffer, MAX_PATH, NULL); errorMsg += messageBuffer; ::MessageBox(m_hWnd, errorMsg, title, MB_OK); } void CFtasrDlg::DisplayResourceResourceError( DWORD ErrorId1, DWORD ErrorId2 ) { CString title, errorMsg1, errorMsg2; title.LoadString(IDS_ERROR_TITLE); errorMsg1.LoadString(ErrorId1); errorMsg2.LoadString(ErrorId2); errorMsg1 += errorMsg2; ::MessageBox(m_hWnd, errorMsg1, title, MB_OK); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// long CFtasrDlg::AddToCommands( FILE* Output ) /*++ Routine Description: This routine registers ftasr in the "Commands" section of the ASR backup file Arguments: Output - Supplies the backup file open in write mode Return Value: ERROR_SUCCESS if the function succeeded. Other error codes for failure --*/ { CHAR string[MAX_PATH]; CHAR lastString[MAX_PATH]; int c, nextNumber; long position, endPosition; PCHAR buffer; size_t amountRead; long r; for (;;) { if (!fgets(string, MAX_PATH, Output)) { return ERROR_INVALID_DATA; } if (!strncmp(string, "[COMMANDS]", 10)) { break; } } strcpy(lastString, ""); nextNumber = 1; for (;;) { c = getc(Output); ungetc(c, Output); if (c == '[') { break; } if (!fgets(string, MAX_PATH, Output)) { return ERROR_INVALID_DATA; } if (string[0] >= '0' && string[0] <= '9') { nextNumber = string[0] - '0' + 1; } } position = ftell(Output); r = fseek(Output, 0, SEEK_END); if (r) { return r; } endPosition = ftell(Output); r = fseek(Output, position, SEEK_SET); if (r) { return r; } buffer = (PCHAR)LocalAlloc(0, endPosition - position + 1); if (!buffer) { return ERROR_NOT_ENOUGH_MEMORY; } amountRead = fread(buffer, 1, endPosition - position + 1, Output); if (!amountRead) { return ERROR_INVALID_DATA; } r = fseek(Output, position + 28, SEEK_SET); if (r) { return r; } if (fwrite(buffer, 1, amountRead, Output) != amountRead) { return ERROR_INVALID_DATA; } r = fseek(Output, position, SEEK_SET); if (r) { return r; } fprintf(Output, "%d=1,2000,1,ftasr,\"restore\"\n", nextNumber); return ERROR_SUCCESS; } long CFtasrDlg::PrintOutVolumeNames( IN FILE* Output, IN FT_LOGICAL_DISK_ID RootLogicalDiskId ) /*++ Routine Description: This routine saves all volume names of a root logical disk in a backup file Arguments: Output - Supplies the backup file open in write mode RootLogicalDiskId - Supplies the id of the root logical disk. Return Value: ERROR_SUCCESS if the function succeeded. Other error codes for failure --*/ { HANDLE h; PMOUNTMGR_MOUNT_POINT point; PMOUNTMGR_MOUNT_POINTS points; BOOL b; DWORD bytes; ULONG i; PWSTR name; WCHAR s; DWORD numNames; long r; h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_NAMES, r); return r; } point = (PMOUNTMGR_MOUNT_POINT)LocalAlloc(0, sizeof(MOUNTMGR_MOUNT_POINT) + sizeof(FT_LOGICAL_DISK_ID)); if (!point) { r = GetLastError(); CloseHandle(h); DisplaySystemError(r); return r; } ZeroMemory(point, sizeof(MOUNTMGR_MOUNT_POINT)); point->UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINT); point->UniqueIdLength = sizeof(FT_LOGICAL_DISK_ID); CopyMemory((PCHAR) point + point->UniqueIdOffset, &RootLogicalDiskId, point->UniqueIdLength); points = (PMOUNTMGR_MOUNT_POINTS)LocalAlloc(0, sizeof(MOUNTMGR_MOUNT_POINTS) + sizeof(WCHAR)); if (!points) { r = GetLastError(); LocalFree(point); CloseHandle(h); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_NAMES, r); return r; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point, sizeof(MOUNTMGR_MOUNT_POINT) + sizeof(FT_LOGICAL_DISK_ID), points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL); while (!b && GetLastError() == ERROR_MORE_DATA) { bytes = points->Size + sizeof(WCHAR); LocalFree(points); points = (PMOUNTMGR_MOUNT_POINTS)LocalAlloc(0, bytes); if (!points) { r = GetLastError(); LocalFree(point); CloseHandle(h); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_NAMES, r); return r; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point, sizeof(MOUNTMGR_MOUNT_POINT) + sizeof(FT_LOGICAL_DISK_ID), points, bytes, &bytes, NULL); } r = GetLastError(); LocalFree(point); CloseHandle(h); if (!b) { LocalFree(points); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_NAMES, r); return r; } numNames = 0; for (i = 0; i < points->NumberOfMountPoints; i++) { point = &points->MountPoints[i]; if (point->SymbolicLinkNameLength != 96) { continue; } name = (PWCHAR) ((PCHAR) points + point->SymbolicLinkNameOffset); if (name[0] == '\\' && name[1] == '?' && name[2] == '?' && name[3] == '\\' && name[4] == 'V' && name[5] == 'o' && name[6] == 'l' && name[7] == 'u' && name[8] == 'm' && name[9] == 'e' && name[10] == '{' && name[19] == '-' && name[24] == '-' && name[29] == '-' && name[34] == '-' && name[47] == '}') { numNames++; } } fprintf(Output, "%d\n", numNames); for (i = 0; i < points->NumberOfMountPoints; i++) { point = &points->MountPoints[i]; if (point->SymbolicLinkNameLength != 96) { continue; } name = (PWCHAR) ((PCHAR) points + point->SymbolicLinkNameOffset); if (name[0] == '\\' && name[1] == '?' && name[2] == '?' && name[3] == '\\' && name[4] == 'V' && name[5] == 'o' && name[6] == 'l' && name[7] == 'u' && name[8] == 'm' && name[9] == 'e' && name[10] == '{' && name[19] == '-' && name[24] == '-' && name[29] == '-' && name[34] == '-' && name[47] == '}') { s = name[48]; name[48] = 0; fprintf(Output,"\""); fwprintf(Output, name); fprintf(Output, "\"\n"); name[48] = s; } } LocalFree(points); return ERROR_SUCCESS; } long CFtasrDlg::PrintOutDiskInfo( IN FILE* Output, IN FT_LOGICAL_DISK_ID LogicalDiskId ) /*++ Routine Description: This routine saves all information needed to restore a logical disk in a backup file Arguments: Output - Supplies the backup file open in write mode LogicalDiskId - Supplies the id of the logical disk. Return Value: ERROR_SUCCESS if the function succeeded. Other error codes for failure --*/ { BOOL b; WORD numMembers; PFT_LOGICAL_DISK_ID members; FT_LOGICAL_DISK_TYPE diskType; LONGLONG volumeSize; WORD i; CHAR configInfo[100]; CHAR stateInfo[100]; PFT_PARTITION_CONFIGURATION_INFORMATION partConfig; PFT_STRIPE_SET_CONFIGURATION_INFORMATION stripeConfig; PFT_MIRROR_SET_CONFIGURATION_INFORMATION mirrorConfig; PFT_STRIPE_SET_WITH_PARITY_CONFIGURATION_INFORMATION swpConfig; PFT_REDISTRIBUTION_CONFIGURATION_INFORMATION redistConfig; long r; fprintf(Output, "%I64X", LogicalDiskId); if (!LogicalDiskId) { fprintf(Output, "\n"); return ERROR_SUCCESS; } b = FtQueryLogicalDiskInformation(LogicalDiskId, NULL, NULL, 0, NULL, &numMembers, 0, NULL, 0, NULL); if (!b) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } if (numMembers) { members = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numMembers*sizeof(FT_LOGICAL_DISK_ID)); if (!members) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } } else { members = NULL; } b = FtQueryLogicalDiskInformation(LogicalDiskId, &diskType, &volumeSize, numMembers, members, &numMembers, 100, configInfo, 100, stateInfo); if (!b) { r = GetLastError(); LocalFree(members); DisplayResourceSystemError(IDS_ERROR_PRINT_VOLUME_INFO, r); return r; } fprintf(Output, ",%d,%d", diskType, numMembers); switch (diskType) { case FtPartition: partConfig = (PFT_PARTITION_CONFIGURATION_INFORMATION) configInfo; fprintf(Output, ",%X,%I64X,%I64X", partConfig->Signature, partConfig->ByteOffset, volumeSize); break; case FtStripeSet: stripeConfig = (PFT_STRIPE_SET_CONFIGURATION_INFORMATION) configInfo; fprintf(Output, ",%X", stripeConfig->StripeSize); break; case FtMirrorSet: mirrorConfig = (PFT_MIRROR_SET_CONFIGURATION_INFORMATION) configInfo; fprintf(Output, ",%I64X", mirrorConfig->MemberSize); break; case FtStripeSetWithParity: swpConfig = (PFT_STRIPE_SET_WITH_PARITY_CONFIGURATION_INFORMATION) configInfo; fprintf(Output, ",%I64X,%X", swpConfig->MemberSize, swpConfig->StripeSize); break; case FtRedistribution: redistConfig = (PFT_REDISTRIBUTION_CONFIGURATION_INFORMATION) configInfo; fprintf(Output, ",%X,%d,%d", redistConfig->StripeSize, redistConfig->FirstMemberWidth, redistConfig->SecondMemberWidth); break; } fprintf(Output, "\n"); for (i = 0; i < numMembers; i++) { r = PrintOutDiskInfo(Output, members[i]); if (r != ERROR_SUCCESS) { LocalFree(members); return r; } } LocalFree(members); return ERROR_SUCCESS; } long CFtasrDlg::Restore( IN FILE* Input ) /*++ Routine Description: This routine restores the logical disks hierarchy using information saved in a backup file Arguments: Input - Supplies the backup file open in read mode Return Value: ERROR_SUCCESS if the restore succeeded. Other error codes for failure --*/ { DWORD numDisks, numNames, i, j, k; PWCHAR* volumeNames; PWCHAR volumeName; int nameLength; FT_LOGICAL_DISK_ID diskId; FT_LOGICAL_DISK_ID expectedPath[MAX_STACK_DEPTH]; long r; m_Progress.SetPos(20); Sleep(300); fwscanf(Input, L"%lu", &numDisks); if (feof(Input)) { numDisks = 0; } if (numDisks > 0) { m_Progress.SetStep(80/numDisks); } for (k = 0; k < numDisks; k++) { DbgPrint(L"ftasr: Start processing disk\n"); fwscanf(Input, L"%d", &numNames); if (feof(Input)) { break; } if (numNames) { volumeNames = (PWCHAR*)LocalAlloc(0, numNames*sizeof(PWCHAR)); if (!volumeNames) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return r; } for (i = 0; i < numNames; i++) { volumeName = (PWCHAR)LocalAlloc(0, MAX_PATH*sizeof(WCHAR)); if (!volumeName) { r = GetLastError(); for (j = 0; j < i; j++) { LocalFree(volumeNames[j]); } LocalFree(volumeNames); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return r; } fwscanf(Input, L"%s", volumeName); nameLength = wcslen(volumeName); if (nameLength < 2 || volumeName[0] != L'\"' || volumeName[nameLength-1] != L'\"') { for (j = 0; j < i; j++) { LocalFree(volumeNames[j]); } LocalFree(volumeNames); LocalFree(volumeName); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME_NAMES, IDS_ERROR_INVALID_BACKUP); return ERROR_INVALID_DATA; } for (j = 0; (long)j < nameLength - 2; j++) { volumeName[j] = volumeName[j+1]; } volumeName[j] = L'\0'; volumeNames[i] = volumeName; } } if (feof(Input)) { break; } diskId = BuildFtDisk(Input, expectedPath, 0, TRUE, NULL, NULL); if (diskId && numNames) { LinkVolumeNamesToLogicalDisk(volumeNames, numNames, diskId); for (i = 0; i < numNames; i++) { LocalFree(volumeNames[i]); } LocalFree(volumeNames); } m_Progress.StepIt(); Sleep(300); } m_Progress.SetPos(101); Sleep(300); return ERROR_SUCCESS; } VOID CFtasrDlg::LinkVolumeNamesToLogicalDisk( IN PWCHAR* VolumeNames, IN DWORD NumNames, IN FT_LOGICAL_DISK_ID LogicalDiskId ) /*++ Routine Description: This links volumes names to a root logical disk Arguments: VolumeNames - Supplies the volume names NumNames - Supplies the number of volume names LogicalDiskId - Supplies the id of the logical disk Return Value: - --*/ { HANDLE h; FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_INPUT inputNtDevice; PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT outputNtDevice; WCHAR bufferNtDevice[MAX_PATH]; ULONG inputQueryPointsSize; PMOUNTMGR_MOUNT_POINT inputQueryPoints; MOUNTMGR_MOUNT_POINTS outputQueryPoints; PMOUNTMGR_TARGET_NAME inputArrival; WCHAR buffer[MAX_PATH]; PMOUNTMGR_CREATE_POINT_INPUT inputCreatePoint; BOOL b; long r; DWORD i; DWORD bytes; // 1. Get the NT device name of the volume inputNtDevice.RootLogicalDiskId = LogicalDiskId; outputNtDevice = (PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT)bufferNtDevice; h = CreateFile(DD_DOS_FT_CONTROL_NAME, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { r = GetLastError(); DbgPrint(L"ftasr: Link volume names failed: Open DD_DOS_FT_CONTROL_NAME failed with %lu\n", r); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return; } b = DeviceIoControl(h, FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK, &inputNtDevice, sizeof(inputNtDevice), outputNtDevice, MAX_PATH * sizeof(WCHAR), &bytes, NULL); r = GetLastError(); CloseHandle(h); if (!b) { DbgPrint(L"ftasr: Link volume names failed: FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK failed with %lu\n", r); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return; } outputNtDevice->NtDeviceName[outputNtDevice->NumberOfCharactersInNtDeviceName] = 0; // 2. Open the mount manager h = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { r = GetLastError(); DbgPrint(L"ftasr: Link volume names failed: Open MOUNTMGR_DOS_DEVICE_NAME failed with %lu\n", r); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return; } // 3. Call IOCTL_MOUNTMGR_QUERY_POINTS to check whether the volume is installed or not inputQueryPointsSize = sizeof(MOUNTMGR_MOUNT_POINT) + outputNtDevice->NumberOfCharactersInNtDeviceName * sizeof(WCHAR); inputQueryPoints = (PMOUNTMGR_MOUNT_POINT)LocalAlloc(0, inputQueryPointsSize); if (!inputQueryPoints) { r = GetLastError(); CloseHandle(h); DbgPrint(L"ftasr: Link volume names failed: Cannot allocate memory for IOCTL_MOUNTMGR_QUERY_POINTS input\n"); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return; } inputQueryPoints->SymbolicLinkNameLength = 0; inputQueryPoints->SymbolicLinkNameOffset = 0; inputQueryPoints->UniqueIdOffset = 0; inputQueryPoints->UniqueIdLength = 0; inputQueryPoints->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); inputQueryPoints->DeviceNameLength = outputNtDevice->NumberOfCharactersInNtDeviceName * sizeof(WCHAR); CopyMemory((PCHAR)inputQueryPoints + inputQueryPoints->DeviceNameOffset, outputNtDevice->NtDeviceName, inputQueryPoints->DeviceNameLength); b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, inputQueryPoints, inputQueryPointsSize, &outputQueryPoints, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL); r = GetLastError(); LocalFree(inputQueryPoints); // 4. If the volume is not installed then force the installation if (!b && r != ERROR_MORE_DATA) { DbgPrint(L"ftasr: Link volume names: IOCTL_MOUNTMGR_QUERY_POINTS failed with %lu\n", r); inputArrival = (PMOUNTMGR_TARGET_NAME)buffer; inputArrival->DeviceNameLength = outputNtDevice->NumberOfCharactersInNtDeviceName * sizeof(WCHAR); CopyMemory((PCHAR)inputArrival->DeviceName, outputNtDevice->NtDeviceName, inputArrival->DeviceNameLength); // Send volume arrival notifications until one of them succeedes. Timeout 2 minutes for (i = 0; i < 1200; i++) { b = DeviceIoControl(h, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, inputArrival, MAX_PATH * sizeof(WCHAR), NULL, 0, &bytes, NULL); if (b) { DbgPrint(L"ftasr: Link volume names: IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION for %s succeeded\n", outputNtDevice->NtDeviceName); break; } r = GetLastError(); DbgPrint(L"ftasr: Link volume names: %lu. IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION for %s failed with %lu\n", i, outputNtDevice->NtDeviceName, r); Sleep(100); } if (!b) { CloseHandle(h); DbgPrint(L"ftasr: Link volume names failed: IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION for %s timed out\n", outputNtDevice->NtDeviceName); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME_NAMES, r); return; } } // 5. Call IOCTL_MOUNTMGR_CREATE_POINT inputCreatePoint = (PMOUNTMGR_CREATE_POINT_INPUT)buffer; for (i = 0; i < NumNames; i++) { inputCreatePoint->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT); inputCreatePoint->SymbolicLinkNameLength = wcslen(VolumeNames[i]) * sizeof(WCHAR); inputCreatePoint->DeviceNameOffset = inputCreatePoint->SymbolicLinkNameOffset + inputCreatePoint->SymbolicLinkNameLength; inputCreatePoint->DeviceNameLength = outputNtDevice->NumberOfCharactersInNtDeviceName * sizeof(WCHAR); CopyMemory((PCHAR)inputCreatePoint + inputCreatePoint->SymbolicLinkNameOffset, VolumeNames[i], inputCreatePoint->SymbolicLinkNameLength); CopyMemory((PCHAR)inputCreatePoint + inputCreatePoint->DeviceNameOffset, outputNtDevice->NtDeviceName, inputCreatePoint->DeviceNameLength); b = DeviceIoControl(h, IOCTL_MOUNTMGR_CREATE_POINT, inputCreatePoint, MAX_PATH * sizeof(WCHAR), NULL, 0, &bytes, NULL); if (b) { DbgPrint(L"ftasr: Link volume names: IOCTL_MOUNTMGR_CREATE_POINT succeeded\n"); } else { DbgPrint(L"ftasr: Link volume names: IOCTL_MOUNTMGR_CREATE_POINT failed with %lu\n", GetLastError()); } } // 6. Close the mount manager CloseHandle(h); } FT_LOGICAL_DISK_ID CFtasrDlg::BuildFtDisk( IN FILE* Input, IN OUT PFT_LOGICAL_DISK_ID ExpectedPath, IN WORD ExpectedPathSize, IN BOOL AllowBreak, OUT PBOOL Existing, /* OPTIONAL */ OUT PBOOL Overwriteable /* OPTIONAL */ ) /*++ Routine Description: This routine restores a logical disk based on the information saved in the backup file Arguments: Input - Supplies the backup file open in read mode ExpectedPath - Supplies the expected forefathers of the logical disk as they appear in the backup file, starting with a root logical volume and ending with the expected parent. The size of this array is maximum MAX_STACK_DEPTH. The array content may change inside this function due to logical disk breakings or replacements ExpectedPathSize - Supplies the number of expected forefathers AllowBreak - Specifies whether the expected forefathers can be broken inside this function Existing - Returns TRUE if the logical disk was already alive and valid at the moment of the execution of this function Overwriteable - Returns TRUE if the FT volume can be overwritten later in the FT ASR process If FALSE we should avoid building an FT set on top of it that may destroy the content of the FT volume Return Value: The id of the logical disk. 0 if the function failed --*/ { FT_LOGICAL_DISK_ID diskId, candidate; FT_LOGICAL_DISK_TYPE diskType, candidateType; DWORD numMembers; WORD candidateNumMembers; DWORD numNewMembers, numInvalidNewMembers; DWORD numNotOverwriteableMembers, notOverwriteableMember; DWORD numReplacedMembers, i, j; PFT_LOGICAL_DISK_ID members; PBOOL existingMembers; ULONG signature; LONGLONG offset, length; DWORD configSize; PVOID config; DWORD stripeSize, firstWidth, secondWidth; DWORDLONG memberSize; FT_STRIPE_SET_CONFIGURATION_INFORMATION stripeConfig; FT_MIRROR_SET_CONFIGURATION_INFORMATION mirrorConfig; FT_STRIPE_SET_WITH_PARITY_CONFIGURATION_INFORMATION swpConfig; FT_REDISTRIBUTION_CONFIGURATION_INFORMATION redistConfig; WCHAR messageBuffer[MAX_PATH]; BOOL ioCapable, b; FT_LOGICAL_DISK_ID realPath[MAX_STACK_DEPTH]; FT_LOGICAL_DISK_ID newRealPath[MAX_STACK_DEPTH]; WORD realPathSize, newRealPathSize; SHORT k; LONGLONG size0, size1; long r; BOOL isSystem, isBoot; DbgPrint(L"ftasr: Build FT disk: "); // Initialization if (Existing) { *Existing = FALSE; } if (Overwriteable) { *Overwriteable = TRUE; } ioCapable = FALSE; candidate = 0; // 1. Read the disk id, type and number of members from the file diskId = 0; fwscanf(Input, L"%I64X", &diskId); if (!diskId) { DbgPrint(L"Error - cannot read DiskId\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_INVALID_BACKUP); return candidate; } DbgPrint(L"DiskId = %I64X\n", diskId); fwscanf(Input, L",%d,%d", &diskType, &numMembers); // 2. Check whether the disk is alive, has the same main parameters and is IO capable b = FtQueryLogicalDiskInformation( diskId, &candidateType, NULL, 0, NULL, &candidateNumMembers, 0, NULL, 0, NULL ); if (b && diskType == candidateType && numMembers == candidateNumMembers) { if (Existing) { *Existing = TRUE; } candidate = diskId; b = FtCheckIo(candidate, &ioCapable); if (!b) { ioCapable = FALSE; } } // 3. Read the configuration information from file. Dismiss the FtPartition case switch (diskType) { case FtPartition: fwscanf(Input, L",%X,%I64X,%I64X", &signature, &offset, &length); if (candidate) { ASSERT(candidate == diskId); DbgPrint(L"ftasr: Build FT disk succeeded - FT partition already exists\n"); return candidate; } candidate = CreateFtPartition(signature, offset, length, &b); // If the new FT partition is not overwritable break the expected path // We try to avoid regenerations that may overwrite the content of the boot/system partition if (!b) { for (i = 0; i < ExpectedPathSize; i++) { if (ExpectedPath[i]) { b = FtBreakLogicalDisk(ExpectedPath[i]); if (b) { DbgPrint(L"ftasr: Expected parent %I64X broken\n", ExpectedPath[i]); ExpectedPath[i] = 0; } else { DbgPrint(L"ftasr: Expected parent %I64X break failed with %ld\n", ExpectedPath[i], GetLastError()); } } } } if (Overwriteable) { *Overwriteable = b; } DbgPrint(L"ftasr: Build FT disk succeeded - new FT partition %I64X\n", candidate); return candidate; break; case FtVolumeSet: configSize = 0; config = NULL; break; case FtStripeSet: fwscanf(Input, L",%X", &stripeSize); stripeConfig.StripeSize = stripeSize; configSize = sizeof(FT_STRIPE_SET_CONFIGURATION_INFORMATION); config = &stripeConfig; break; case FtMirrorSet: fwscanf(Input, L",%I64X", &memberSize); mirrorConfig.MemberSize = memberSize; configSize = sizeof(FT_MIRROR_SET_CONFIGURATION_INFORMATION); config = &mirrorConfig; break; case FtStripeSetWithParity: fwscanf(Input, L",%I64X,%X", &memberSize, &stripeSize); ZeroMemory(&swpConfig, sizeof(swpConfig)); swpConfig.MemberSize = memberSize; swpConfig.StripeSize = stripeSize; configSize = sizeof(FT_STRIPE_SET_WITH_PARITY_CONFIGURATION_INFORMATION); config = &swpConfig; break; case FtRedistribution: fwscanf(Input, L",%X,%d,%d", &stripeSize, &firstWidth, &secondWidth); redistConfig.StripeSize = stripeSize | 0x80000000; redistConfig.FirstMemberWidth = (USHORT) firstWidth; redistConfig.SecondMemberWidth = (USHORT) secondWidth; configSize = sizeof(FT_REDISTRIBUTION_CONFIGURATION_INFORMATION); config = &redistConfig; break; default: ASSERT(!candidate); DbgPrint(L"ftasr: Build FT disk failed - unrecognized logical disk type\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_INVALID_BACKUP); return 0; } // 4. Build the members members = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numMembers*sizeof(FT_LOGICAL_DISK_ID)); if (!members) { DbgPrint(L"ftasr: Build FT disk failed - allocation failed for members array\n"); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME, GetLastError()); return candidate; } existingMembers = (PBOOL)LocalAlloc(0, numMembers*sizeof(BOOL)); if (!existingMembers) { DbgPrint(L"ftasr: Build FT disk failed - allocation failed for existing members array\n"); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME, GetLastError()); LocalFree(members); return candidate; } if (ExpectedPathSize >= MAX_STACK_DEPTH) { // that's really a huge, huge stack! DbgPrint(L"ftasr: Build FT disk failed - expected path size overpassed\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_UNEXPECTED); goto _cleanup; } ExpectedPath[ExpectedPathSize] = candidate; // kind of push in a stack numNewMembers = 0; numInvalidNewMembers = 0; numNotOverwriteableMembers = 0; for (i = 0; i < numMembers; i++) { members[i] = BuildFtDisk(Input, ExpectedPath, (WORD)(ExpectedPathSize + 1), AllowBreak && !ioCapable, &(existingMembers[i]), &b); if (!existingMembers[i]) { DbgPrint(L"ftasr: New member %I64X\n", members[i]); numNewMembers++; } if (!members[i]) { numInvalidNewMembers++; } if (!b && members[i] && !existingMembers[i]) { numNotOverwriteableMembers++; notOverwriteableMember = i; } } ASSERT(numInvalidNewMembers <= numNewMembers); // 5. See what happened with the candidate during step 4 // It might have been broken or replaced, so we must update the candidate candidate = ExpectedPath[ExpectedPathSize]; // kind of pop from a stack if (Existing) { *Existing = (candidate != 0); } // 6. "Use Candidate" - check whether the candidate is intact, can be repaired or should be broken if (candidate) { ASSERT(!Existing || *Existing); // 6.1. All members are intact -> return candidate if (!numNewMembers) { DbgPrint(L"ftasr: Build FT disk succeeded - all members are intact\n"); goto _cleanup; } // 6.2. "Replace members" - We have new valid members waiting to replace old invalid members if (numNewMembers > numInvalidNewMembers) { // 6.2.1. Get the path from the candidate to its root volume b = GetPathFromLogicalDiskToRoot(candidate, realPath, &realPathSize); if (!b) { // Unexpected error. Cannot take the risk to replace members or break the disk DbgPrint(L"ftasr: Build FT disk failed - GetPathFromLogicalDiskToRoot failed\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_UNEXPECTED); goto _cleanup; } // 6.2.2. Call replace for every new valid member numReplacedMembers = 0; for (i = 0; i < numMembers; i++) { if (!existingMembers[i] && members[i] != 0) { DbgPrint(L"ftasr: Replace member %d with logical disk %I64X\n", i, members[i]); b = FtReplaceLogicalDiskMember(candidate, (WORD)i, members[i], &diskId); if (b) { candidate = diskId; numReplacedMembers++; DbgPrint(L"ftasr: Replacement succeeded. Id = %I64X\n", candidate); } else { DbgPrint(L"ftasr: Replacement failed with %ld\n", GetLastError()); } } } // 6.2.3. If at least one replacement succeeded update the expected path if (numReplacedMembers > 0) { b = GetPathFromLogicalDiskToRoot(candidate, newRealPath, &newRealPathSize); ASSERT(!b || realPathSize == newRealPathSize); // Stop synchronization at this moment if (b) { if (newRealPathSize > 0 ) { FtStopSyncOperations(newRealPath[newRealPathSize - 1]); } else { FtStopSyncOperations(candidate); } } // Update the expected path. Some of the expected parents may have been changed // due to replacements performed for(i = 0; i < ExpectedPathSize; i++) { if (ExpectedPath[i]) { for(j = 0; j < realPathSize; j++) { if (ExpectedPath[i] == realPath[j]) { ExpectedPath[i] = b ? newRealPath[j] : 0; break; } } } } } // 6.2.4. If all replacements succeeded return the candidate if (numReplacedMembers == numNewMembers - numInvalidNewMembers) { DbgPrint(L"ftasr: Build FT disk succeeded - all replacements succeeded\n"); goto _cleanup; } } // End "Replace members" // 6.3. "Break" - Check whether we are allowed to break the candidate. If we aren't, return it. b = FtCheckIo(candidate, &ioCapable); if (!b) { ioCapable = FALSE; } if (!AllowBreak || ioCapable || numInvalidNewMembers > 0 || !GetPathFromLogicalDiskToRoot(candidate, realPath, &realPathSize) ) { // Cannot break or doesn't make sense to break DbgPrint(L"ftasr: Build FT disk failed - cannot break or doesn't make sense to break\n"); goto _cleanup; } // Check whether the real path matches with the expected path // Note: real path is a reversed path for (i = 0; i < realPathSize; i++) { if (i >= ExpectedPathSize || realPath[i] != ExpectedPath[ExpectedPathSize - i - 1]) { DbgPrint(L"ftasr: Build FT disk failed - real path doesn't match with the expected path\n"); goto _cleanup; } } for (k = (SHORT)realPathSize - 1; k >= 0; k--){ DbgPrint(L"ftasr: Break parent %I64X\n", realPath[k]); b = FtBreakLogicalDisk(realPath[k]); if (!b) { DbgPrint(L"ftasr: Build FT disk failed - parent break failed with %ld\n", GetLastError()); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_UNEXPECTED); goto _cleanup; } for (i = 0; i < ExpectedPathSize; i++) { if (ExpectedPath[i] == realPath[k]) { ExpectedPath[i] = 0; break; } } } DbgPrint(L"ftasr: Break candidate %I64X\n", candidate); b = FtBreakLogicalDisk(candidate); if (!b) { DbgPrint(L"ftasr: Build FT disk - candidate break failed with %ld\n", GetLastError()); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_UNEXPECTED); goto _cleanup; } candidate = 0; if (Existing) { *Existing = FALSE; } // End "Break" } // End "Use candidate" // 7. Take care of not overwriteable members if (numNotOverwriteableMembers > 0) { ASSERT(candidate == 0); if (diskType != FtMirrorSet || numNotOverwriteableMembers > 1) { DbgPrint(L"ftasr: Build FT disk failed - too many not overwriteable members\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_MORE_FORMATTED); goto _cleanup; } // Make sure the not overwriteable member is the winner of the mirror set if (notOverwriteableMember > 0) { ASSERT(notOverwriteableMember == 1); diskId = members[0]; members[0] = members[notOverwriteableMember]; members[notOverwriteableMember] = diskId; } } // At this moment we have to create a brand new logical disk ASSERT(candidate == 0); ASSERT(!Existing || !(*Existing)); // 8. Check whether all members are valid if (numInvalidNewMembers > 0) { DbgPrint(L"ftasr: Build FT disk failed - invalid members\n"); candidate = 0; goto _cleanup; } // 9. If we create a stripe or a swp take every old member and // delete its file system info /* //If we create a stripe or a swp take every old member and call //DeleteFileSystemInfo(). //This is to prevent those annoying popups "The file or directory ... is //corrupt and unreadable." if (diskType == FtStripeSet || diskType == FtStripeSetWithParity) { for (i = 0; i < numMembers; i++) { if (existingMembers[i]) { ASSERT(members[i]); if (DeleteFileSystemInfo(members[i])) { DbgPrint(L"ftasr: Build FT disk - file system info deleted successfully for %I64X\n", members[i]); } else { DbgPrint(L"ftasr: Build FT disk - delete file system info failed for %I64X\n", members[i]); } } } } */ // 10. If we try to mirror the boot volume make sure the second member is // at least as large as the boot volume (which should be the first member // because of step 7) if (diskType == FtMirrorSet) { ASSERT(members[0]); ASSERT(members[1]); r = IsSystemOrBootVolume(members[0], &isSystem, &isBoot); if( r != ERROR_SUCCESS) { DbgPrint(L"ftasr: Build FT disk failed - IsSystemOrBootVolume failed with %lu", r); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME, r); goto _cleanup; } if (isBoot) { if (!FtQueryLogicalDiskInformation(members[0], NULL, &size0, 0, NULL, NULL, 0, NULL, 0, NULL) || !FtQueryLogicalDiskInformation(members[1], NULL, &size1, 0, NULL, NULL, 0, NULL, 0, NULL) || (size0 > size1) ) { DbgPrint(L"ftasr: Build FT disk - cannot mirror the boot volume\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_MIRROR_BOOT_VOLUME); goto _cleanup; } } } // 11. "Create" - No candidate.Create a brand new logical disk if (!FtCreateLogicalDisk(diskType, (USHORT) numMembers, members, (USHORT) configSize, config, &candidate)) { DbgPrint(L"ftasr: Build FT disk failed - logical disk creation failed\n"); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME, GetLastError()); candidate = 0; goto _cleanup; } DbgPrint(L"ftasr: Build FT disk succeeded - logical disk created successfully. DiskId = %I64X\n", candidate); // Stop synchronization at this moment FtStopSyncOperations(candidate); if (Overwriteable) { *Overwriteable = (numNotOverwriteableMembers == 0); } _cleanup: LocalFree(members); LocalFree(existingMembers); return candidate; } FT_LOGICAL_DISK_ID CFtasrDlg::CreateFtPartition( IN ULONG Signature, IN LONGLONG Offset, IN LONGLONG Length, OUT PBOOL Overwriteable ) /*++ Routine Description: This routine creates an FT partition based on a disk partition Arguments: Signature - Supplies the signature of the disk partition Offset - Supplies the offset of the disk partition Length - Supplies the length of the disk partition Overwriteable - Returns TRUE if the partition can be overwritten later in the FT ASR process If FALSE we should avoid building an FT set on top of it that may destroy the content of the partition FALSE if the partition is already formatted and contains data. Return Value: The id of the FT partition. 0 if the creation failed --*/ { ULONG s; LONGLONG o, l, bestDelta, newDelta; HANDLE h, hh, best; WCHAR volumeName[MAX_PATH], bestVolName[MAX_PATH]; FT_LOGICAL_DISK_ID diskId; long r; BOOL b; WCHAR fileSystem[100]; DbgPrint(L"ftasr: Create FT partition: Signature = %lX, Offset = %I64X, Length = %I64X\n", Signature, Offset, Length); *Overwriteable = TRUE; h = FindFirstVolume(volumeName, MAX_PATH); if (h == INVALID_HANDLE_VALUE) { DbgPrint(L"ftasr: Create FT partition failed - FindFirstVolume failed\n"); DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_PARTITION_NOT_FOUND); return 0; } bestDelta = -1; for (;;) { volumeName[lstrlen(volumeName) - 1] = 0; hh = CreateFile(volumeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hh == INVALID_HANDLE_VALUE) { DbgPrint(L"ftasr: Create FT partition failed - Volume opening failed\n"); continue; } if (QueryPartitionInformation(hh, &s, &o, &l) && s == Signature && l >= Length) { if (o > Offset) { newDelta = o - Offset; } else { newDelta = Offset - o; } if (bestDelta == -1 || newDelta < bestDelta) { if (bestDelta >= 0 ) { CloseHandle(best); } bestDelta = newDelta; best = hh; wcscpy(bestVolName, volumeName); } else { CloseHandle(hh); } } else { CloseHandle(hh); } if (!FindNextVolume(h, volumeName, MAX_PATH)) { break; } } FindVolumeClose(h); if (bestDelta < 0) { DisplayResourceResourceError(IDS_ERROR_RESTORE_VOLUME, IDS_ERROR_PARTITION_NOT_FOUND); return 0; } b = FtCreatePartitionLogicalDisk(best, &diskId); r = GetLastError(); CloseHandle(best); if (!b) { DbgPrint(L"ftasr: Create FT partition failed - FtCreatePartitionLogicalDisk failed\n"); DisplayResourceSystemError(IDS_ERROR_RESTORE_VOLUME, r); return 0; } // Check whether the partition is formatted wcscat(bestVolName, L"\\"); b = GetVolumeInformation(bestVolName, NULL, 0, NULL, NULL, NULL, fileSystem, 100); if (b) { DbgPrint(L"ftasr: Create FT partition - GetVolumeInformation returned %s\n", fileSystem); if (wcscmp(fileSystem, L"RAW")) { *Overwriteable = FALSE; } } else { r = GetLastError(); DbgPrint(L"ftasr: Create FT partition - GetVolumeInformation failed with %lu\n", r); } DbgPrint(L"ftasr: Create FT partition succeeded: DiskId = %I64X %s\n", diskId, *Overwriteable ? L"" : L"Formatted"); return diskId; } BOOL CFtasrDlg::QueryPartitionInformation( IN HANDLE Handle, OUT PDWORD Signature, OUT PLONGLONG Offset, OUT PLONGLONG Length ) /*++ Routine Description: This routine returns the signature, offset and length of a partition Arguments: Handle - Supplies a handle to an open partition Signature - Returns the signature of the partition Offset - Returns the offset of the partition Length - Returns the length of the partition Return Value: TRUE if the query succeeded --*/ { BOOL b; STORAGE_DEVICE_NUMBER number; DWORD bytes; WCHAR diskName[MAX_PATH]; HANDLE h; PDRIVE_LAYOUT_INFORMATION layout; PARTITION_INFORMATION partInfo; b = DeviceIoControl(Handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &number, sizeof(number), &bytes, NULL); if (!b || number.PartitionNumber == -1 || number.PartitionNumber == 0) { return FALSE; } swprintf(diskName, L"\\\\.\\PhysicalDrive%d", number.DeviceNumber); h = CreateFile(diskName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { return FALSE; } layout = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc(0, 4096); if (!layout) { CloseHandle(h); return FALSE; } b = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, layout, 4096, &bytes, NULL); if (!b) { LocalFree(layout); CloseHandle(h); return FALSE; } *Signature = layout->Signature; LocalFree(layout); CloseHandle(h); b = DeviceIoControl(Handle, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(partInfo), &bytes, NULL); if (!b) { return b; } *Offset = partInfo.StartingOffset.QuadPart; *Length = partInfo.PartitionLength.QuadPart; return b; } /****************************************************************************************************/ /* */ /* Method FtGetParentLogicalDisk should be a ftapi call based on a ftdisk IOCTL. When this will */ /* happen the next two methods will disappear from this code. */ /* */ /****************************************************************************************************/ BOOL CFtasrDlg::GetParentLogicalDiskInVolume( IN FT_LOGICAL_DISK_ID VolumeId, IN FT_LOGICAL_DISK_ID LogicalDiskId, OUT PFT_LOGICAL_DISK_ID ParentId ) /*++ Routine Description: This routine search for a logical disk inside a (sub)volume and returns the logical disk id of its parent. Arguments: VolumeId - Supplies the id of the (sub)volume. LogicalDiskId - Supplies the id of the logical disk. ParentId - Returns the id of the parent. 0 if the disk is not found inside the volume. Return Value: FALSE - Failure to search for the given disk. TRUE - The search succeeded. The value returned in ParentId is valid --*/ { WORD numberOfMembers, i; PFT_LOGICAL_DISK_ID members; FT_LOGICAL_DISK_ID parentId; BOOL b, result; *ParentId = 0; b = FtQueryLogicalDiskInformation( VolumeId, NULL, NULL, 0, NULL, &numberOfMembers, 0, NULL, 0, NULL); if (!b) { return FALSE; } if (numberOfMembers > 0 ) { members = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numberOfMembers * sizeof(FT_LOGICAL_DISK_ID)); if (!members) { return FALSE; } b = FtQueryLogicalDiskInformation( VolumeId, NULL, NULL, numberOfMembers, members, &numberOfMembers, 0, NULL, 0, NULL); if (!b) { LocalFree(members); return FALSE; } } else { members = NULL; } result = TRUE; for (i = 0; i < numberOfMembers; i++) { if (members[i] == LogicalDiskId) { *ParentId = VolumeId; result = TRUE; break; } b = GetParentLogicalDiskInVolume(members[i], LogicalDiskId, &parentId); if (!b) { result = FALSE; continue; } if (parentId) { *ParentId = parentId; result = TRUE; break; } } if (members) { LocalFree(members); } return result; } BOOL CFtasrDlg::FtGetParentLogicalDisk( IN FT_LOGICAL_DISK_ID LogicalDiskId, OUT PFT_LOGICAL_DISK_ID ParentId ) /*++ Routine Description: This routine returns the parent of a logical disk Arguments: LogicalDiskId - Supplies the id of the logical disk. ParentId - Returns the id of the parent. 0 if the disk doesn't exist or has no parent Return Value: FALSE - Failure to search for the given disk. TRUE - The search succeeded. The value returned in ParentId is valid --*/ { BOOL b, result; PFT_LOGICAL_DISK_ID diskId; FT_LOGICAL_DISK_ID parentId; DWORD numDisks, i; *ParentId = 0; b = FtEnumerateLogicalDisks(0, NULL, &numDisks); if (!b) { return FALSE; } if (numDisks > 0) { diskId = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numDisks*sizeof(FT_LOGICAL_DISK_ID)); if (!diskId) { return FALSE; } b = FtEnumerateLogicalDisks(numDisks, diskId, &numDisks); if (!b) { LocalFree(diskId); return FALSE; } } else { diskId = NULL; } result = TRUE; for (i = 0; i < numDisks; i++) { if (diskId[i] == LogicalDiskId) { result = TRUE; break; } b = GetParentLogicalDiskInVolume(diskId[i], LogicalDiskId, &parentId); if (!b) { result = FALSE; continue; } if (parentId) { *ParentId = parentId; result = TRUE; break; } } if (diskId) { LocalFree(diskId); } return result; } BOOL CFtasrDlg::GetPathFromLogicalDiskToRoot( IN FT_LOGICAL_DISK_ID LogicalDiskId, OUT PFT_LOGICAL_DISK_ID Path, OUT PWORD PathSize ) /*++ Routine Description: This routine returns all forefathers of a logical disk starting with its parent and ending with a root logical disk Arguments: LogicalDiskId - Supplies the id of the logical disk Path - Returns the forefathers of the logical disk. The size of the array is maximum MAX_STACK_DEPTH PathSize - Returns the number of forefathers Return Value: TRUE if all forefathers were found and added to Path --*/ { FT_LOGICAL_DISK_ID diskId; WORD i; BOOL b; diskId = LogicalDiskId; for (i = 0; i < MAX_STACK_DEPTH; i++) { b = FtGetParentLogicalDisk(diskId, &(Path[i])); if (!b) { return FALSE; } diskId = Path[i]; if (!diskId) { break; } } if (i >= MAX_STACK_DEPTH) { return FALSE; } *PathSize = i; return TRUE; } /*********************************************************************************************/ /* Some methods used to check for FT configurations not supported by ftasr at this moment */ /*********************************************************************************************/ #ifdef CHECK_UNSUPPORTED_CONFIG long CFtasrDlg::CheckForUnsupportedConfig( IN DWORD NumDisks, IN PFT_LOGICAL_DISK_ID ArrayOfDiskId ) /*++ Routine Description: This routine check if the current FT configuration is supported by ASR. Supported configurations are: - Mirrored boot and system partition filling the entire disk - Mirrored boot partition and mirrored system partition, saharing the same disk and filling it completely - Data volume sets whose members fill their disks completely - Data stripe sets whose members fill their disks completely Arguments: NumDisks - Supplies the number of root FT volumes ArrayOfDiskId - Supplies the Id's of the root FT volumes Return Value: ERROR_SUCCESS if the restore succeeded. Other error codes for failure --*/ { DWORD i, j; FT_LOGICAL_DISK_ID diskId; WORD numMembers; PFT_LOGICAL_DISK_ID members; FT_LOGICAL_DISK_TYPE diskType; BOOL b; BOOL isSystem; BOOL isBoot; long r; FT_LOGICAL_DISK_TYPE memberType; LONGLONG memberSize; CHAR configInfo[100]; PFT_PARTITION_CONFIGURATION_INFORMATION partConfig; DISK_GEOMETRY geometry; LONGLONG cylinderSize; WORD numMirrors = 0; DWORD lastMirrorFirstMemberDiskNumber; LONGLONG lastMirrorFirstMemberByteOffset; LONGLONG lastMirrorFirstMemberSize; for (i = 0; i < NumDisks; i++) { diskId = ArrayOfDiskId[i]; b = FtQueryLogicalDiskInformation(diskId, &diskType, NULL, 0, NULL, &numMembers, 0, NULL, 0, NULL); if (!b) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } switch(diskType) { case FtMirrorSet: case FtVolumeSet: case FtStripeSet: break; case FtPartition: DisplayResourceResourceError(IDS_ERROR_FTPARTITION_DETECTED, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; case FtStripeSetWithParity: DisplayResourceResourceError(IDS_ERROR_SWP_DETECTED, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; case FtRedistribution: DisplayResourceResourceError(IDS_ERROR_REDISTRIBUTION_DETECTED, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; default: DisplayResourceError(IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } r = IsSystemOrBootVolume(diskId, &isSystem, &isBoot); if (r != ERROR_SUCCESS) { DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } if(diskType == FtMirrorSet) { if (!isSystem && !isBoot) { DisplayResourceResourceError(IDS_ERROR_NOT_BOOT_SYSTEM_MIRROR, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } } else { // Volume set or stripe set if (isSystem || isBoot) { DisplayResourceResourceError(IDS_ERROR_BOOT_SYSTEM_NOT_MIRROR, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } } members = (PFT_LOGICAL_DISK_ID)LocalAlloc(0, numMembers*sizeof(FT_LOGICAL_DISK_ID)); if (!members) { r = GetLastError(); DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } b = FtQueryLogicalDiskInformation(diskId, &diskType, NULL, numMembers, members, &numMembers, 0, NULL, 0, NULL); if (!b) { r = GetLastError(); LocalFree(members); DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } for (j = 0; j < numMembers; j++) { b = FtQueryLogicalDiskInformation(members[j], &memberType, &memberSize, 0, NULL, NULL, 100, configInfo, 0, NULL); if (!b) { r = GetLastError(); LocalFree(members); DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } if (memberType != FtPartition) { LocalFree(members); DisplayResourceResourceError(IDS_ERROR_STACK, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } partConfig = (PFT_PARTITION_CONFIGURATION_INFORMATION)configInfo; if ((diskType != FtMirrorSet) || (j == 0)) { r = GetDiskGeometry(partConfig->DiskNumber, &geometry); if (r != ERROR_SUCCESS) { LocalFree(members); DisplayResourceSystemError(IDS_ERROR_CHECK_FAILURE, r); return r; } cylinderSize = geometry.TracksPerCylinder * geometry.SectorsPerTrack * geometry.BytesPerSector; if((diskType != FtMirrorSet) || (isSystem && isBoot)) { if (partConfig->ByteOffset < cylinderSize && partConfig->ByteOffset + memberSize >= (geometry.Cylinders.QuadPart - 1) * cylinderSize) { // It's OK. The first member of the boot/system mirror fills the entire disk } else { LocalFree(members); switch(diskType) { case FtVolumeSet: DisplayResourceResourceError(IDS_ERROR_VOLUME_SET_SIZE, IDS_ERROR_UNSUPPORTED_CONFIGURATION); break; case FtStripeSet: DisplayResourceResourceError(IDS_ERROR_STRIPE_SET_SIZE, IDS_ERROR_UNSUPPORTED_CONFIGURATION); break; case FtMirrorSet: DisplayResourceResourceError(IDS_ERROR_MIRROR_SET_SIZE, IDS_ERROR_UNSUPPORTED_CONFIGURATION); break; } return ERROR_NOT_SUPPORTED; } } else { // Mirror set boot OR system first member if (numMirrors == 0) { numMirrors++; lastMirrorFirstMemberDiskNumber = partConfig->DiskNumber; lastMirrorFirstMemberByteOffset = partConfig->ByteOffset; lastMirrorFirstMemberSize = memberSize; } else { // numMirrors == 1 We cannot find more than one boot and one system mirror numMirrors++; if (lastMirrorFirstMemberDiskNumber != partConfig->DiskNumber) { LocalFree(members); DisplayResourceResourceError(IDS_ERROR_MIRRORS_ON_DIFFERENT_DISKS, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } if ( ( partConfig->ByteOffset < cylinderSize && partConfig->ByteOffset + memberSize + cylinderSize > lastMirrorFirstMemberByteOffset && lastMirrorFirstMemberByteOffset + lastMirrorFirstMemberSize >= (geometry.Cylinders.QuadPart - 1) * cylinderSize ) || ( lastMirrorFirstMemberByteOffset < cylinderSize && lastMirrorFirstMemberByteOffset + lastMirrorFirstMemberSize + cylinderSize > partConfig->ByteOffset && partConfig->ByteOffset + memberSize >= (geometry.Cylinders.QuadPart - 1) * cylinderSize ) ) { // It's OK. The first members of the two mirrors fill the entire disk } else { LocalFree(members); DisplayResourceResourceError(IDS_ERROR_MIRRORS_SIZES, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } } } } } // members loop LocalFree(members); } // disks loop if (numMirrors == 1) { DisplayResourceResourceError(IDS_ERROR_ONE_MIRROR, IDS_ERROR_UNSUPPORTED_CONFIGURATION); return ERROR_NOT_SUPPORTED; } return ERROR_SUCCESS; } #define DOSDEV_PREFIX L"\\DosDevices\\" long CFtasrDlg::IsSystemOrBootVolume( IN FT_LOGICAL_DISK_ID DiskId, OUT PBOOL IsSystem, OUT PBOOL IsBoot ) /*++ Routine Description: This routine check if a FT root volume is the boot and/or system volume Arguments: DiskId - Supplies the Id of the root FT volume IsSystem - Returns TRUE if the volume contains the NT loader files IsBoot - Returns TRUE if the volume contains the NT root Return Value: ERROR_SUCCESS if the restore succeeded. Other error codes for failure --*/ { HANDLE h; FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_INPUT input1; ULONG output1Size; PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT output1; BOOL b; ULONG bytes; long r; HKEY key = NULL ; DWORD disposition; WCHAR system[MAX_PATH]; DWORD valueSize ; DWORD type ; WCHAR ntPath[MAX_PATH] ; ULONG input2Size; PMOUNTMGR_MOUNT_POINT input2; ULONG output2Size; PMOUNTMGR_MOUNT_POINTS output2; ULONG i; PWSTR symName; *IsSystem = FALSE; *IsBoot = FALSE; // First get the NT volume name of the logical disk h = CreateFile(L"\\\\.\\FtControl", GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE){ return GetLastError(); } input1.RootLogicalDiskId = DiskId; output1Size = MAX_PATH; output1 = (PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT)LocalAlloc(0, output1Size); if (!output1) { r = GetLastError(); CloseHandle(h); return r; } b = DeviceIoControl(h, FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK, &input1, sizeof(input1), output1, output1Size, &bytes, NULL); r = GetLastError(); CloseHandle(h); if (!b) { LocalFree(output1); return r; } //output1->NtDeviceName, //output1->NumberOfCharactersInNtDeviceName output1->NtDeviceName[output1->NumberOfCharactersInNtDeviceName] = L'\0'; // Second get the NT volume name of the system partition from HKLM:\SYSTEM\Setup r = RegCreateKeyEx( HKEY_LOCAL_MACHINE, L"SYSTEM\\SETUP", 0, NULL, 0, KEY_READ, NULL, &key, &disposition); if (r != ERROR_SUCCESS) { LocalFree(output1); return r; } valueSize = sizeof(system) ; r = RegQueryValueEx(key, L"SystemPartition", NULL, &type, (PBYTE)system, &valueSize); if (r != ERROR_SUCCESS) { LocalFree(output1); return r; } // Compare the logical disk NT volume name with the system volume name if (!wcscmp(output1->NtDeviceName, system)) { *IsSystem = TRUE; } // Third get the boot volume path b = GetEnvironmentVariable(L"SystemRoot", ntPath, MAX_PATH); if (!b) { LocalFree(output1); return ERROR_SUCCESS; } // Then compare its drive letter with our logical disk drive letter h = CreateFile( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); if (h == INVALID_HANDLE_VALUE) { r = GetLastError(); LocalFree(output1); return r; } input2Size = sizeof(MOUNTMGR_MOUNT_POINT) + ((output1->NumberOfCharactersInNtDeviceName + 1)*sizeof(WCHAR)); input2 = (PMOUNTMGR_MOUNT_POINT)LocalAlloc(0, input2Size); if (!input2) { r = GetLastError(); CloseHandle(h); LocalFree(output1); return r; } input2->SymbolicLinkNameLength = 0; input2->SymbolicLinkNameOffset = 0; input2->UniqueIdOffset = 0; input2->UniqueIdLength = 0; input2->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); input2->DeviceNameLength = output1->NumberOfCharactersInNtDeviceName * sizeof(WCHAR); wcscpy((PWSTR)((PCHAR)input2 + input2->DeviceNameOffset), output1->NtDeviceName); LocalFree(output1); output2Size = sizeof(MOUNTMGR_MOUNT_POINTS) + 1024; output2 = (PMOUNTMGR_MOUNT_POINTS)LocalAlloc(0, output2Size); if (!output2) { r = GetLastError(); CloseHandle(h); LocalFree(input2); return r; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, input2, input2Size, output2, output2Size, &bytes, NULL); r = GetLastError(); while (!b && r == ERROR_MORE_DATA) { output2Size = output2->Size; LocalFree(output2); output2 = (PMOUNTMGR_MOUNT_POINTS)LocalAlloc(0, output2Size); if (!output2) { r = GetLastError(); CloseHandle(h); LocalFree(input2); return r; } b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, input2, input2Size, output2, output2Size, &bytes, NULL); r = GetLastError(); } CloseHandle(h); LocalFree(input2); if (!b) { LocalFree(output2); return r; } for (i = 0; i < output2->NumberOfMountPoints; i++) { symName = (PWSTR)( (PCHAR)output2 + output2->MountPoints[i].SymbolicLinkNameOffset) ; if (!wcsncmp(DOSDEV_PREFIX, symName, wcslen(DOSDEV_PREFIX))) { if (ntPath[0] == symName[wcslen(DOSDEV_PREFIX)]) { *IsBoot = TRUE; } } } LocalFree(output2); return ERROR_SUCCESS; } long CFtasrDlg::GetDiskGeometry( ULONG DiskNumber, PDISK_GEOMETRY Geometry ) /*++ Routine Description: This routine retrieves the geometry of the given disk Arguments: DiskNumber - Supplies the disk number (0, 1, 2 ...) Geometry - Returns the disk geometry Return Value: ERROR_SUCCESS if the restore succeeded. Other error codes for failure --*/ { WCHAR name[50]; ULONG bytes; HANDLE h; BOOL b; long r; wsprintf(name, L"\\\\.\\PHYSICALDRIVE%lu", DiskNumber); h = CreateFile(name, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0 , INVALID_HANDLE_VALUE ); if (h == INVALID_HANDLE_VALUE) { return GetLastError(); } b = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, Geometry, sizeof(DISK_GEOMETRY), &bytes, NULL ); r = GetLastError(); CloseHandle(h); if (!b) { return r; } return ERROR_SUCCESS; } #endif // #ifdef CHECK_UNSUPPORTED_CONFIG /* BOOL CFtasrDlg::DeleteFileSystemInfo( IN FT_LOGICAL_DISK_ID LogicalDiskId ) { // This should be done only for partitions. // We have to fill the first sector of the partition with zeroes. There is // the file system info stored. //1. Open the volume READ & WRITE, FILE_SHARE_EVERYTHING .... //2. GET_DISK_GEOMETRY ---> get SectorSize //3. Allocate a buffer 2*SectorSize //4. Choose the first address in the buffer which is multiple of sector size //5. Fill the buffer with zeroes //6. DeviceIoControl FSCTL_LOCK_VOLUME //7. SetFilePointer to the beginning of the file //8. WriteFile (address multiple of SectorSize in the buffer, SectorSize) //9. CloseHandle //If one of them doesn't work, don't worry! All that can happen is to keep //getting those annoying popups } */