/*++ Copyright (c) 1998 Microsoft Corporation Module Name: FTMan File Name: Global.cpp Abstract: Implementation of useful global functions Author: Cristian Teodorescu October 29, 1998 Notes: Revision History: --*/ #include "stdafx.h" #include "FTUtil.h" #include "Global.h" #include "Item.h" #include "MainFrm.h" #include "Resource.h" #include #include #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////////////////////// // Display functions /* Global function: DisplaySystemErrorMessage Purpose: Display the last system error message prefixed ( or not ) with another string taken from our resources explaining the context of the failure Parameters: [IN] UINT unContextMsgID The ID of the string that must prefix the system error message. ID in resource.h Default is 0 which means that the system error shouldn't be prefixed Return value: TRUE if the functions succeeds */ BOOL DisplaySystemErrorMessage( UINT unContextMsgID /* =0 */) { MY_TRY // Get the system error message LPVOID lpMsgBuf; if( !::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL ) ) // Process any inserts in lpMsgBuf. return FALSE; CString str; if( unContextMsgID != 0) // The system error message must be prefixed with another string from the resources of this application if( !str.LoadString( unContextMsgID ) ) return FALSE; str += ((LPCTSTR)lpMsgBuf); LocalFree( lpMsgBuf ); AfxMessageBox(str, MB_ICONSTOP); return TRUE; MY_CATCH_AND_THROW } /* Global function: DisplayStatusBarMessage (1) Purpose: Displays a message in the first pane of the main frame status bar Parameters: [IN] LPCTSTR lpszMsg The message to display Return value: TRUE if the functions succeeds */ BOOL DisplayStatusBarMessage( LPCTSTR lpszMsg ) { MY_TRY CStatusBar* pStatusBar = ((CMainFrame*)AfxGetMainWnd())->GetStatusBar(); if( !pStatusBar ) return FALSE; // Use the ID_SEPARATOR pane of the status bar return pStatusBar->SetPaneText( 5, lpszMsg ); MY_CATCH_AND_THROW } /* Global function: DisplayStatusBarMessage (2) Purpose: Display a message in the first pane of the main frame status bar Parameters: [IN] UINT unMsgID Resource ID of the string to display Return value: TRUE if the functions succeeds */ BOOL DisplayStatusBarMessage( UINT unMsgID ) { MY_TRY CString str; str.LoadString(unMsgID); return DisplayStatusBarMessage(str); MY_CATCH_AND_THROW } /* Global function: FormatVolumeSize Purpose: Format the size of a volume in a "readable" way ( in GB, MB or KB depending on the size range ) Parameters: [OUT] CString& strSize Reference to the string to receive the formatted size [IN] LONGLONG llSize The size to format ( in Bytes ) Return value: - */ void FormatVolumeSize( CString& strSize, LONGLONG llSize ) { MY_TRY if( llSize >= 0x40000000 ) // 1GB = 2^30 B strSize.Format(_T("%.2f GB"), ((double)(llSize>>20))/((double)0x400)); else if (llSize >= 0x100000 ) // 1MB = 2^20 B strSize.Format(_T("%.2f MB"), ((double)(llSize>>10))/((double)0x400)); else strSize.Format(_T("%.2f KB"), ((double)llSize)/((double)0x400)); MY_CATCH_AND_THROW } /* Global function: CopyW2Str Purpose: Copy a Unicode character array into a CString Parameters: [OUT] CString& strDest Reference to the string to receive the formatted size [IN] LPWSTR strSource Unicode character array to be copied [IN] ULONG ulLength The number of characters to copy Return value: - */ void CopyW2Str( CString& strDest, LPWSTR strSource, ULONG ulLength ) { MY_TRY LPTSTR lpstr = strDest.GetBuffer( ulLength + 1 ); #ifdef UNICODE memcpy( lpstr, strSource, ulLength * sizeof(WCHAR ) ); #else WideCharToMultiByte( CP_APP, 0, strSource, ulLength, lpstr, ulLength * sizeof(char), NULL, NULL ); #endif lpstr[ulLength]=_T('\0'); strDest.ReleaseBuffer(); MY_CATCH_AND_THROW } /* Global function: CopyStr2W Purpose: Copy a CString content into a Unicode character array Parameters: [OUT] LPWSTR strDest Pointer to the buffer to receive the character array. It should be already allocated [IN] CString& strSource Reference to the string to be copied Return value: - */ void CopyStr2W( LPWSTR strDest, CString& strSource ) { MY_TRY LPTSTR lpstr = strSource.GetBuffer(0); int nSize = strSource.GetLength(); #ifdef UNICODE memcpy( strDest, lpstr, nSize * sizeof(WCHAR) ); #else MultiByteToWideChar( CP_APP, 0, lpstr, nSize, strDest, nSize * sizeof(WCHAR) ); #endif strDest[nSize] = _T('\0'); strSource.ReleaseBuffer(); MY_CATCH_AND_THROW } /* Global function: OpenVolume Purpose: Open a volume given its name The name must be like this "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\" Parameters: [IN] CString& strVolumeName Volume name Return value: HANDLE Handle of the open volume. INVALID_HANDLE_VALUE if the operation failed */ HANDLE OpenVolume( const CString& strVolumeName ) { return CreateFile( strVolumeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); } /* Global function: IsDOSName Purpose: Decides wether a name is a DOS name i.e. "\DosDevices\X:" Parameters: [IN] CString& str The name Return value: TRUE if it is a DOS name */ BOOL IsDOSName( const CString& str ) { MY_TRY // "\DosDevices\X:" return( ( str.GetLength() == 14 ) && ( str.Left(12) == _T("\\DosDevices\\") ) && ( str[13] == _T(':') ) ); MY_CATCH_AND_THROW } /* Global function: IsVolumeName Purpose: Decides wether a name is a volume name i.e. "\??\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" Parameters: [IN] CString& str The name Return value: TRUE if it is a volume name */ BOOL IsVolumeName( const CString& str ) { MY_TRY // "\??\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" return( ( str.GetLength() == 48 ) && ( str.Left(11) == _T("\\??\\Volume{") ) && ( str[19] == _T('-') ) && ( str[24] == _T('-') ) && ( str[29] == _T('-') ) && ( str[34] == _T('-') ) && ( str[47] == _T('}') ) ); MY_CATCH_AND_THROW } /* Global function: QueryDriveLetterAndVolumeName Purpose: Query the drive letter and the name of the volume given its NT Name The name will be like this: "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" Parameters: [IN] CString& strNTName The NT name of the volume [OUT] TCHAR& cDriveLetter The drive letter of the volume [OUT] CString& strVolumeName The name ( to be used by FindFirst/FindNext ) of the volume Return value: TRUE if the function succeeded */ BOOL QueryDriveLetterAndVolumeName( CString& strNTName, TCHAR& cDriveLetter, CString& strVolumeName ) { MY_TRY cDriveLetter = _T('\0'); strVolumeName = _T(""); if( strNTName.IsEmpty() ) return FALSE; HANDLE h; ULONG ulInputSize; PMOUNTMGR_MOUNT_POINT pInput; ULONG ulOutputSize; PMOUNTMGR_MOUNT_POINTS pOutput; ULONG ulBytes; BOOL bResult; USHORT unNTNameLength; // Open the mount manager h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { TRACE( _T("Error: Open mount manager failed in GetDriveLetterAndVolumeName\n") ); return FALSE; } // Prepare the input structure unNTNameLength = (USHORT)strNTName.GetLength(); ulInputSize = sizeof(MOUNTMGR_MOUNT_POINT) + ((unNTNameLength + 1)*sizeof(WCHAR)); pInput = (PMOUNTMGR_MOUNT_POINT) LocalAlloc( 0, ulInputSize); if (!pInput) { TRACE( _T("Error: Memory allocation failure in GetDriveLetterAndVolumeName\n") ); CloseHandle(h); return FALSE; } pInput->SymbolicLinkNameLength = 0; pInput->SymbolicLinkNameOffset = 0; pInput->UniqueIdOffset = 0; pInput->UniqueIdLength = 0; pInput->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); pInput->DeviceNameLength = unNTNameLength * sizeof(WCHAR); CopyStr2W( (LPWSTR) ((PUCHAR) pInput + pInput->DeviceNameOffset), strNTName ); // Prepare the output structure ulOutputSize = sizeof(MOUNTMGR_MOUNT_POINTS) + 1024; pOutput = (PMOUNTMGR_MOUNT_POINTS) LocalAlloc( 0, ulOutputSize); if (!pOutput) { TRACE( _T("Error: Memory allocation failure in GetDriveLetterAndVolumeName\n") ); CloseHandle(h); LocalFree(pInput); return FALSE; } bResult = DeviceIoControl( h, IOCTL_MOUNTMGR_QUERY_POINTS, pInput, ulInputSize, pOutput, ulOutputSize, &ulBytes, NULL); while (!bResult && GetLastError() == ERROR_MORE_DATA) { ulOutputSize = pOutput->Size; LocalFree( pOutput ); pOutput = (PMOUNTMGR_MOUNT_POINTS)(LocalAlloc(0, ulOutputSize )); if (!pOutput) { TRACE( _T("Error: Memory Allocation failure in QueryMountPoints") ); CloseHandle(h); LocalFree(pInput); return FALSE; } bResult = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, pInput, ulInputSize, pOutput, ulOutputSize, &ulBytes, NULL); } CloseHandle(h); LocalFree(pInput); if( !bResult ) { TRACE( _T("Error: IOCTL_MOUNTMGR_QUERY_POINTS failure in GetDriveLetterAndVolumeName\n") ); LocalFree(pOutput); return FALSE; } //CStringArray arrMountPointName; CString strMountPoint; for( ULONG i=0; i < pOutput->NumberOfMountPoints; i++ ) { PMOUNTMGR_MOUNT_POINT pMountPoint = &(pOutput->MountPoints[i]); /* if (!point->SymbolicLinkNameOffset) { continue; } */ CopyW2Str( strMountPoint, (LPWSTR)((PCHAR)pOutput + pMountPoint->SymbolicLinkNameOffset), pMountPoint->SymbolicLinkNameLength / sizeof(WCHAR) ); if( IsDOSName( strMountPoint ) ) // i.e. "\DosDevices\X:" { // I got the drive letter cDriveLetter = strMountPoint[12]; } else if ( IsVolumeName( strMountPoint ) ) // i.e. "\??\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" { // I got the volume name !!!! strVolumeName = strMountPoint; // Make it like this : "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" strVolumeName.SetAt( 1, _T('\\') ); } } LocalFree(pOutput); return TRUE; MY_CATCH_AND_THROW } /* Global function: QueryMountListInPath Purpose: Query the mount manager for all mount paths of the given volumes in a certain path Each found mount path is added to the corresponding volume in array arrItems ( if any ) Parameters: [IN] CString& strPath The path to search in [IN/OUT] CObArray& arrVolumesData Array of CItemData - Tree items ( volumes ) whose mount paths we are looking for Return value: - */ void QueryMountListInPath( const CString& strPath, CObArray& arrVolumesData ) { MY_TRY BOOL bResult; CString strVolName, strVolMountPoint, strMountPointPath; LPTSTR lpstr = strVolName.GetBuffer(MAX_PATH); bResult = GetVolumeNameForVolumeMountPoint( strPath, lpstr, MAX_PATH ); strVolName.ReleaseBuffer(); if( !bResult ) return; // Cut the final backslash of the volume name and search it in the volumes array CString strVolName2 = strVolName.Left( strVolName.GetLength() - 1 ); for( int i = 0; i < arrVolumesData.GetSize(); i++ ) { CItemData* pData = (CItemData*)(arrVolumesData[i]); if( pData->GetVolumeName() == strVolName2 ) { // Cut the final backslash of the path and add it to the volume's mount paths CString strPath2 = strPath.Left( strPath.GetLength() - 1 ); pData->GetMountPaths().Add(strPath2); } } lpstr = strVolMountPoint.GetBuffer(MAX_PATH); HANDLE h = FindFirstVolumeMountPoint( strVolName, lpstr, MAX_PATH); strVolMountPoint.ReleaseBuffer(); if( h == INVALID_HANDLE_VALUE ) return; for( ; ; ) { strMountPointPath = strPath + strVolMountPoint; QueryMountListInPath( strMountPointPath, arrVolumesData ); lpstr = strVolMountPoint.GetBuffer(MAX_PATH); bResult = FindNextVolumeMountPoint( h, lpstr, MAX_PATH ); strVolMountPoint.ReleaseBuffer(); if( !bResult ) break; } FindVolumeMountPointClose(h); MY_CATCH_AND_THROW } /* Global function: QueryMountList Purpose: Query the mount manager for all mount paths of the given volumes Each found mount path is added to the corresponding volume in array arrItems ( if any ) Parameters: [IN/OUT] CObArray& arrVolumesData Array of CItemData - Tree items ( volumes ) whose mount paths we are looking for Return value: - */ void QueryMountList( CObArray& arrVolumesData ) { MY_TRY CString strName = _T("X:\\"); for( TCHAR cDriveLetter = _T('A'); cDriveLetter <= _T('Z'); cDriveLetter++ ) { strName.SetAt(0, cDriveLetter); QueryMountListInPath( strName, arrVolumesData ); } MY_CATCH_AND_THROW } /* Global function: ConvertPartitionsToFT Purpose: Scans an array of CItemData and converts all physical partitions to FT partitions. Then return the logical volume ID's of all items in the array Parameters: [IN] CObArray& arrVolumeData The array of CItemData to scan [OUT] FT_LOGICAL_DISK_ID* arrVolID The array of logical volume ID's of all items from arrVolumeData ( arrVolID[i] is the logical volume ID of volume represented by arrVolumeData[i] ) Return value: TRUE if all physical partitions are converted succesfully If one conversion fails then all previously converted partitions are deconverted */ BOOL ConvertPartitionsToFT( CObArray& arrVolumeData, FT_LOGICAL_DISK_ID* arrVolID ) { MY_TRY ASSERT( arrVolID ); for( int i = 0; i < arrVolumeData.GetSize(); i++ ) { CItemData* pData = (CItemData*)(arrVolumeData[i]); ASSERT(pData); if( pData->GetItemType() == IT_LogicalVolume ) pData->GetVolumeID( arrVolID[i] ); else if( pData->GetItemType() == IT_PhysicalPartition ) { ASSERT( !pData->GetVolumeName().IsEmpty() ); if( !FTPart( pData->GetVolumeName(), pData->GetDriveLetter(), &(arrVolID[i]) ) ) { // Deconvert all partitions you've previously converted and return FALSE DeconvertPartitionsFromFT( arrVolumeData, arrVolID, i ); return FALSE; } } else ASSERT(FALSE); } return TRUE; MY_CATCH_AND_THROW } /* Global function: DeconvertPartitionsFromFT Purpose: Scans an array of CItemData and deconverts all physical partitions from FT partitions. Parameters: [IN] CObArray& arrVolumeData The array of CItemData to scan [IN] FT_LOGICAL_DISK_ID* arrVolID The array of logical volume ID's of volumes from arrVolumeData ( arrVolID[i] is the logical volume ID of volume represented by arrVolumeData[i] ) [IN] int nItems The number of items ( starting with offset zero ) to deconvert If nItems = -1 then all items in the array will be scanned and deconverted Return value: TRUE if all physical partitions are deconverted succesfully */ BOOL DeconvertPartitionsFromFT( CObArray& arrVolumeData, FT_LOGICAL_DISK_ID* arrVolID, int nItems /* =-1 */ ) { MY_TRY BOOL bResult = TRUE; if( nItems == -1 ) nItems = (int)arrVolumeData.GetSize(); for( int i = 0; i < nItems; i++ ) { CItemData* pData = (CItemData*)(arrVolumeData[i]); ASSERT(pData); if( pData->GetItemType() == IT_PhysicalPartition ) bResult = FTBreak( arrVolID[i] ) && bResult; } return bResult; MY_CATCH_AND_THROW } /* Global function: CheckAdministratorsMembership Purpose: Checks whether the current user is a member of the Administrators' group Parameters: [OUT] BOOL& bIsAdministrator Returns TRUE if the user is a member of the administrators group Return value: TRUE the check concluded successfully with a YES / NO answer */ BOOL CheckAdministratorsMembership( BOOL& bIsAdministrator ) { MY_TRY BYTE sidBuffer[100]; PSID pSID = (PSID)&sidBuffer; SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY; // Create a SID for the BUILTIN\Administrators group. if( !AllocateAndInitializeSid( &SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID) ) { TRACE(_T("Error in AllocateAndInitializeSid\n")); DisplaySystemErrorMessage( IDS_ERR_CHECK_ADMINISTRATOR ); return FALSE; } if( !CheckTokenMembership( NULL, pSID, &bIsAdministrator ) ) { TRACE(_T("Error in CheckTokenMembership\n")); DisplaySystemErrorMessage( IDS_ERR_CHECK_ADMINISTRATOR ); return FALSE; } if (pSID) FreeSid(pSID); return TRUE; MY_CATCH_AND_THROW }