/*++ Copyright (c) 1998 Microsoft Corporation Module Name: FTMan File Name: FTUtil.cpp Abstract: Implementation of FT utilities Author: Cristian Teodorescu October 29, 1998 Notes: Revision History: --*/ #include "stdafx.h" #include "DiskMap.h" #include "FTUtil.h" #include "Global.h" #include "Item.h" #include "Resource.h" extern "C" { #include } #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /* Global function: FTBreak Purpose: Break a logical volume Parameters: [IN] FT_LOGICAL_DISK_ID llVolID ID of the logical volume Return value: TRUE if the volume is broken successfully */ BOOL FTBreak( FT_LOGICAL_DISK_ID llVolID ) { MY_TRY CWaitCursor wc; BOOL bResult; DisplayStatusBarMessage( IDS_STATUS_FTBREAK ); bResult = FtBreakLogicalDisk( llVolID ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTBREAK, MB_ICONINFORMATION ); } else DisplaySystemErrorMessage( IDS_ERR_FTBREAK ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTChkIO Purpose: Check the IO status of a logical volume Parameters: [IN] FT_LOGICAL_DISK_ID llVolID ID of the logical volume [OUT] BOOL* pbOK The result of the IO check Return value: TRUE if the IO check operation succeeded */ BOOL FTChkIO( FT_LOGICAL_DISK_ID llVolID, BOOL* pbOK ) { MY_TRY BOOL bResult; bResult = FtCheckIo( llVolID, pbOK ); /* if( bResult ) { CString strMsg, strOK; strOK.LoadString( bOK ? IDS_OK : IDS_NOTOK ); AfxFormatString1(strMsg, IDS_MSG_FTCHKIO, strOK); AfxMessageBox(strMsg,MB_ICONINFORMATION); } else DisplaySystemErrorMessage( IDS_ERR_FTCHKIO ); */ return bResult; MY_CATCH_AND_THROW } /* Global function: FTExtend Purpose: Extend the file system of a volume to the maximum possible Parameters: [IN] FT_LOGICAL_DISK_ID llVolID ID of the logical volume Return value: TRUE if the file system is extended successfully */ BOOL FTExtend( FT_LOGICAL_DISK_ID llVolID ) { MY_TRY CWaitCursor wc; BOOL bReturn; HANDLE h; PARTITION_INFORMATION partInfo; DISK_GEOMETRY geometry; ULONG ulBytes; LONGLONG llNewSectors; CString strVolumeName; if( !FTQueryVolumeName( llVolID, strVolumeName ) ) return FALSE; h = CreateFile( strVolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); if( h == INVALID_HANDLE_VALUE ) { DisplaySystemErrorMessage( IDS_ERR_FTEXTEND ); return FALSE; } bReturn = DeviceIoControl( h, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(partInfo), &ulBytes, NULL ); if( !bReturn ) { DisplaySystemErrorMessage( IDS_ERR_FTEXTEND ); CloseHandle(h); return FALSE; } bReturn = DeviceIoControl( h, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof(geometry), &ulBytes, NULL ); if( !bReturn ) { DisplaySystemErrorMessage( IDS_ERR_FTEXTEND ); CloseHandle(h); return FALSE; } llNewSectors = partInfo.PartitionLength.QuadPart / geometry.BytesPerSector; bReturn = DeviceIoControl( h, FSCTL_EXTEND_VOLUME, &llNewSectors, sizeof(llNewSectors), NULL, 0, &ulBytes, NULL ); CloseHandle(h); return bReturn; MY_CATCH_AND_THROW } /* Global function: FTInit Purpose: Initialize a logical volume with repairing ( or not ) the orphan member Parameters: [IN] FT_LOGICAL_DISK_ID llVolID The ID of the logical volume [BOOL] bInitOrphans Initialize the orphan member or not Return value: TRUE if the logical volume was initialized successfully */ BOOL FTInit( FT_LOGICAL_DISK_ID llVolID, BOOL bInitOrphans /* = TRUE */ ) { MY_TRY CWaitCursor wc; BOOL bResult; DisplayStatusBarMessage( IDS_STATUS_FTINIT ); bResult = FtInitializeLogicalDisk( llVolID, bInitOrphans ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTINIT, MB_ICONINFORMATION ); } else DisplaySystemErrorMessage( IDS_ERR_FTINIT ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTMirror Purpose: Create a mirror set based on the given logical volumes Parameters: [IN] FT_LOGICAL_DISK_ID* arrVolID ID's of the members [IN] WORD wNumVols The number of members ( must be 2 ) [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new mirror set is to be stored If NULL then don't return the ID Return value: TRUE if the mirror set is created successfully */ BOOL FTMirror( FT_LOGICAL_DISK_ID* arrVolID, WORD wNumVols, FT_LOGICAL_DISK_ID* pllVolID /* = NULL */ ) { MY_TRY ASSERT( arrVolID ); ASSERT( wNumVols == 2 ); CWaitCursor wc; BOOL bResult; FT_MIRROR_SET_CONFIGURATION_INFORMATION configInfo; FT_LOGICAL_DISK_ID llNewVolID; LONGLONG llMemberSize, llZeroMemberSize; DisplayStatusBarMessage( IDS_STATUS_FTMIRROR ); configInfo.MemberSize = MAXLONGLONG; for( int i = 0; i < wNumVols; i++ ) { bResult = FtQueryLogicalDiskInformation( arrVolID[i], NULL, &llMemberSize, 0, NULL, NULL, 0, NULL, 0, NULL ); if( !bResult ) { DisplaySystemErrorMessage(IDS_ERR_RETRIEVING_VOL_INFO); return FALSE; } if( llMemberSize < configInfo.MemberSize ) configInfo.MemberSize = llMemberSize; if( i == 0 ) llZeroMemberSize = llMemberSize; } if( llMemberSize < llZeroMemberSize ) { CString str, strSize1, strSize2; FormatVolumeSize( strSize1, llZeroMemberSize ); FormatVolumeSize( strSize2, llMemberSize ); AfxFormatString2(str, IDS_QST_FTMIRROR, strSize1, strSize2); if( IDYES != AfxMessageBox( str, MB_YESNO | MB_DEFBUTTON2 ) ) { DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return FALSE; } wc.Restore(); } bResult = FtCreateLogicalDisk( FtMirrorSet, wNumVols, arrVolID, sizeof(configInfo), &configInfo, &llNewVolID ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTMIRROR, MB_ICONINFORMATION ); if( pllVolID ) *pllVolID = llNewVolID; } else DisplaySystemErrorMessage( IDS_ERR_FTMIRROR ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTOrphan Purpose: Orphan the given member of the given logical volume. Parameters: [IN] FT_LOGICAL_DISK_ID llVolID ID of the logical volume [IN] WORD wMember Zero-based index of the member to be orphaned Return value: TRUE if the member is orphaned successfully */ BOOL FTOrphan( FT_LOGICAL_DISK_ID llVolID, WORD wMember ) { MY_TRY CWaitCursor wc; BOOL bResult; DisplayStatusBarMessage( IDS_STATUS_FTORPHAN ); bResult = FtOrphanLogicalDiskMember( llVolID, wMember ); if( bResult ) { // AfxMessageBox( IDS_MSG_FTORPHAN, MB_ICONINFORMATION ); } else DisplaySystemErrorMessage( IDS_ERR_FTORPHAN ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTPart Purpose: Converts a physical partition into a FT partition Parameters: [IN] CString& strVolumeName Volume name of the physical partition It should be like this: "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" [IN] TCHAR cDriveLetter The drive letter of the physical partition '\0' if none [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new FT partitions to be stored If NULL then don't return the ID Return value: TRUE if the FT partition is created successfully */ BOOL FTPart( const CString& strVolumeName, TCHAR cDriveLetter /* =_T('\0') */, FT_LOGICAL_DISK_ID* pllVolID /* = NULL */ ) { MY_TRY CWaitCursor wc; BOOL bResult; HANDLE hPart; FT_LOGICAL_DISK_ID llVolID; // First open the partition hPart = OpenVolume( strVolumeName ); if( hPart == INVALID_HANDLE_VALUE ) { DisplaySystemErrorMessage( IDS_ERR_FTPART ); return FALSE; } // Second create the logical volume bResult = FtCreatePartitionLogicalDisk( hPart, &llVolID ); CloseHandle(hPart); if( bResult ) { //AfxMessageBox( IDS_MSG_FTPART, MB_ICONINFORMATION ); if( cDriveLetter != _T('\0') ) { #ifdef UNICODE cDriveLetter = (WCHAR)(towupper( cDriveLetter )); #else cDriveLetter = (char)(toupper( cDriveLetter )); #endif FtSetStickyDriveLetter( llVolID, (UCHAR) cDriveLetter ); } if( pllVolID ) *pllVolID = llVolID; } else DisplaySystemErrorMessage( IDS_ERR_FTPART ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTRegen Purpose: Replace a member of a logical volume with another logical volume and start the regeneration process. Parameters: [IN] FT_LOGICAL_DISK_ID llVolID ID of the logical volume whose member is to be replaced [IN] WORD wMember Zero-based index of the member to be replced [IN] FT_LOGICAL_DISK_ID llReplVolID ID of the replacement [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new set is to be stored If NULL then don't return the ID Return value: TRUE if the stripe set is created successfully */ BOOL FTRegen( FT_LOGICAL_DISK_ID llVolID, WORD wMember, FT_LOGICAL_DISK_ID llReplVolID, FT_LOGICAL_DISK_ID* pllVolID /* =NULL */ ) { MY_TRY CWaitCursor wc; BOOL bResult; FT_LOGICAL_DISK_ID llNewVolID; DisplayStatusBarMessage( IDS_STATUS_FTREGEN ); bResult = FtReplaceLogicalDiskMember( llVolID, wMember, llReplVolID, &llNewVolID ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTREGEN, MB_ICONINFORMATION ); if( pllVolID ) *pllVolID = llNewVolID; } else DisplaySystemErrorMessage( IDS_ERR_FTREGEN ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTStripe Purpose: Create a stripe set based on the given logical volumes Parameters: [IN] FT_LOGICAL_DISK_ID* arrVolID ID's of the members [IN] WORD wNumVols The number of members [IN] LONGLONG llStripeSize The size of the stripe chunks [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new stripe set is to be stored If NULL then don't return the ID Return value: TRUE if the stripe set is created successfully */ BOOL FTStripe( FT_LOGICAL_DISK_ID* arrVolID, WORD wNumVols, ULONG ulStripeSize, FT_LOGICAL_DISK_ID* pllVolID /* = NULL */) { MY_TRY ASSERT( arrVolID ); ASSERT( wNumVols >= 2 ); CWaitCursor wc; BOOL bResult; FT_STRIPE_SET_CONFIGURATION_INFORMATION configInfo; FT_LOGICAL_DISK_ID llNewVolID; DisplayStatusBarMessage( IDS_STATUS_FTSTRIPE ); configInfo.StripeSize = ulStripeSize; bResult = FtCreateLogicalDisk( FtStripeSet, wNumVols, arrVolID, sizeof(configInfo), &configInfo, &llNewVolID ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTSTRIPE, MB_ICONINFORMATION ); if( pllVolID ) *pllVolID = llNewVolID; } else DisplaySystemErrorMessage( IDS_ERR_FTSTRIPE ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTSWP Purpose: Create a stripe set with parity based on the given logical volumes Parameters: [IN] FT_LOGICAL_DISK_ID* arrVolID ID's of the members [IN] WORD wNumVols The number of members [IN] LONGLONG llStripeSize The size of the stripe chunks [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new stripe set with parity is to be stored If NULL then don't return the ID Return value: TRUE if the stripe set with parity is created successfully */ BOOL FTSWP( FT_LOGICAL_DISK_ID* arrVolID, WORD wNumVols, ULONG ulStripeSize, FT_LOGICAL_DISK_ID* pllVolID /* = NULL */ ) { MY_TRY ASSERT( arrVolID ); ASSERT( wNumVols >= 3 ); CWaitCursor wc; BOOL bResult; FT_STRIPE_SET_WITH_PARITY_CONFIGURATION_INFORMATION configInfo; FT_LOGICAL_DISK_ID llNewVolID; LONGLONG llMemberSize; DisplayStatusBarMessage( IDS_STATUS_FTSWP ); configInfo.MemberSize = MAXLONGLONG; for( int i=0; i < wNumVols; i++ ) { bResult = FtQueryLogicalDiskInformation( arrVolID[i], NULL, &llMemberSize, 0, NULL, NULL, 0, NULL, 0, NULL ); if( !bResult ) { DisplaySystemErrorMessage(IDS_ERR_RETRIEVING_VOL_INFO); return FALSE; } if( llMemberSize < configInfo.MemberSize ) configInfo.MemberSize = llMemberSize; } configInfo.StripeSize = ulStripeSize; bResult = FtCreateLogicalDisk( FtStripeSetWithParity, wNumVols, arrVolID, sizeof( configInfo), &configInfo, &llNewVolID ); if( bResult ) { //AfxMessageBox(IDS_MSG_FTSWP, MB_ICONINFORMATION); if( pllVolID ) *pllVolID = llNewVolID; } else DisplaySystemErrorMessage(IDS_ERR_FTSWP); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } /* Global function: FTVolSet Purpose: Create a volume set based on the given logical volumes Parameters: [IN] FT_LOGICAL_DISK_ID* arrVolID ID's of the members [IN] WORD wNumVols The number of members [OUT] FT_LOGICAL_DISK_ID* pllVolID Address where the logical volume ID of the new volume set is to be stored If NULL then don't return the ID Return value: TRUE if the volume set is created successfully */ BOOL FTVolSet( FT_LOGICAL_DISK_ID* arrVolID, WORD wNumVols, FT_LOGICAL_DISK_ID* pllVolID /* = NULL */) { MY_TRY ASSERT( arrVolID ); ASSERT( wNumVols >= 2 ); CWaitCursor wc; BOOL bResult; FT_LOGICAL_DISK_ID llNewVolID; DisplayStatusBarMessage( IDS_STATUS_FTVOLSET ); bResult = FtCreateLogicalDisk( FtVolumeSet, wNumVols, arrVolID, 0, NULL, &llNewVolID ); if( bResult ) { //AfxMessageBox( IDS_MSG_FTVOLSET, MB_ICONINFORMATION ); if( pllVolID ) *pllVolID = llNewVolID; } else DisplaySystemErrorMessage( IDS_ERR_FTVOLSET ); DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE ); return bResult; MY_CATCH_AND_THROW } BOOL FTQueryNTDeviceName( FT_LOGICAL_DISK_ID llVolID, CString& strNTName ) { MY_TRY CWaitCursor wc; HANDLE h; FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_INPUT Input; ULONG ulOutputSize; PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT pOutput; BOOL b; ULONG ulBytes; h = CreateFile(_T("\\\\.\\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 FALSE; Input.RootLogicalDiskId = llVolID; ulOutputSize = MAX_PATH; pOutput = (PFT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK_OUTPUT)LocalAlloc(0, ulOutputSize); if (!pOutput) { CloseHandle(h); return FALSE; } b = DeviceIoControl(h, FT_QUERY_NT_DEVICE_NAME_FOR_LOGICAL_DISK, &Input, sizeof(Input), pOutput, ulOutputSize, &ulBytes, NULL); CloseHandle(h); if (!b ) { LocalFree(pOutput); return FALSE; } CopyW2Str( strNTName, pOutput->NtDeviceName, pOutput->NumberOfCharactersInNtDeviceName ); LocalFree(pOutput); return TRUE; MY_CATCH_AND_THROW } /* Global function: FTGetDisksSet Purpose: Retrieves all disks the logical volume is located on Parameters: [IN] FT_LOGICAL_DISK_ID llVolID The ID of the logical volume [OUT] CULONGSet& setDisks The set of disks Return value: TRUE if the functions succeeds */ BOOL FTGetDisksSet( FT_LOGICAL_DISK_ID llVolID, CULONGSet& setDisks ) { MY_TRY CWaitCursor wc; FT_LOGICAL_DISK_TYPE nVolType; LONGLONG llVolSize; WORD numMembers; FT_LOGICAL_DISK_ID members[100]; CHAR stateInfo[100]; CHAR configInfo[100]; setDisks.RemoveAll(); // Read all information related to this logical volume BOOL b = FtQueryLogicalDiskInformation ( llVolID, &nVolType, &llVolSize, 100, members, &numMembers, sizeof(configInfo), &configInfo, sizeof(stateInfo), &stateInfo ); if(!b) return FALSE; if( nVolType == FtPartition ) { setDisks.Add( ((PFT_PARTITION_CONFIGURATION_INFORMATION)configInfo)->DiskNumber ); return TRUE; } // The disks set is the reunion of all members disk sets for( WORD i = 0; i < numMembers; i++ ) { CULONGSet setMemberDisks; if( !FTGetDisksSet( members[i], setMemberDisks ) ) return FALSE; setDisks += setMemberDisks; } return TRUE; MY_CATCH_AND_THROW } /* Global function: FTQueryVolumeName Purpose: Retrieve the volume name of a logical volume The volume name should be like this: "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" Parameters: [IN] FT_LOGICAL_DISK_ID llVolID The ID of the logical volume [OUT] CString& strVolumeName Reference to a string to receive the volume name Return value: TRUE if the functions succeeds */ BOOL FTQueryVolumeName( FT_LOGICAL_DISK_ID llVolID, CString& strVolumeName ) { MY_TRY CWaitCursor wc; CString strNTName; if (!FTQueryNTDeviceName( llVolID, strNTName ) ) return FALSE; TCHAR cDriveLetter; return QueryDriveLetterAndVolumeName( strNTName, cDriveLetter, strVolumeName ); MY_CATCH_AND_THROW } /* Global function: FTDelete Purpose: Delete a logical volume by deleting all its physical partitions Parameters: [IN] FT_LOGICAL_DISK_ID llVolID The ID of the logical volume Return value: TRUE if all its physical partitions were deleted */ BOOL FTDelete( FT_LOGICAL_DISK_ID llVolID ) { MY_TRY CWaitCursor wc; FT_LOGICAL_DISK_TYPE nVolType; LONGLONG llVolSize; WORD numMembers; FT_LOGICAL_DISK_ID members[100]; CHAR stateInfo[100]; CHAR configInfo[100]; // Read all information related to this logical volume BOOL b = FtQueryLogicalDiskInformation ( llVolID, &nVolType, &llVolSize, 100, members, &numMembers, sizeof(configInfo), &configInfo, sizeof(stateInfo), &stateInfo ); if(!b) { ::DisplaySystemErrorMessage( IDS_ERR_RETRIEVING_VOL_INFO ); return FALSE; } if( nVolType == FtPartition ) { CDiskMap diskMap( ((PFT_PARTITION_CONFIGURATION_INFORMATION)configInfo)->DiskNumber ); return diskMap.DeletePartition( ((PFT_PARTITION_CONFIGURATION_INFORMATION)configInfo)->ByteOffset ); } // Delete all members BOOL bResult = TRUE; for( WORD i = 0; i < numMembers; i++ ) bResult = FTDelete( members[i] ) && bResult; return bResult; MY_CATCH_AND_THROW }