1706 lines
52 KiB
C++
1706 lines
52 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FTMan
|
|
|
|
File Name:
|
|
|
|
DiskMap.cpp
|
|
|
|
Abstract:
|
|
|
|
Implementation of classes used to keep the disk array map in memory. Used in retrieving partitions and free spaces,
|
|
in creating and deleting partitions
|
|
|
|
Author:
|
|
|
|
Cristian Teodorescu October 23, 1998
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "DiskMap.h"
|
|
#include "FrSpace.h"
|
|
#include "PhPart.h"
|
|
#include "Resource.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
//#define CSEC_FAT 32680
|
|
//#define CSEC_FAT32MEG 65536
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CDiskMap
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Public methods
|
|
|
|
/*
|
|
Public method: LoadDiskInfo
|
|
|
|
Purpose: Load the disk layout and geometry into this object
|
|
|
|
Parameters: [OUT] CString& strErrors
|
|
All errors found during LoadDiskInfo are reported through this string
|
|
[OUT] BOOL& bMissingDisk
|
|
Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
|
|
This may notify the calling procedure that the search for installed disks is over
|
|
|
|
Return value: TRUE if the disk information is loaded successfully
|
|
*/
|
|
|
|
BOOL CDiskMap::LoadDiskInfo(
|
|
CString& strErrors,
|
|
BOOL& bMissingDisk )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
m_bLoaded = FALSE;
|
|
strErrors = _T("");
|
|
bMissingDisk = FALSE;
|
|
|
|
|
|
CString strFileName;
|
|
strFileName.Format(_T("\\\\.\\PHYSICALDRIVE%lu"), m_dwDiskNumber);
|
|
|
|
// Try to open the disk
|
|
HANDLE hDisk = CreateFile(
|
|
strFileName, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0 , INVALID_HANDLE_VALUE );
|
|
if( hDisk == INVALID_HANDLE_VALUE )
|
|
{
|
|
// This disk is not installed in the system
|
|
bMissingDisk = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
// Read the geometry of the disk
|
|
ULONG ulBytes;
|
|
DISK_GEOMETRY geometry;
|
|
if( !DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
|
|
&geometry, sizeof(geometry), &ulBytes, NULL ) )
|
|
{
|
|
AddError( strErrors, IDS_ERR_GET_DRIVE_GEOMETRY, TRUE );
|
|
CloseHandle( hDisk );
|
|
return FALSE;
|
|
}
|
|
|
|
m_llSectorSize = geometry.BytesPerSector;
|
|
m_llTrackSize = geometry.SectorsPerTrack * m_llSectorSize;
|
|
m_llCylinderSize = geometry.TracksPerCylinder * m_llTrackSize;
|
|
m_llDiskSize = geometry.Cylinders.QuadPart * m_llCylinderSize;
|
|
|
|
// Allocate some space for the drive layout buffer
|
|
if( m_pBuffer == NULL )
|
|
{
|
|
ASSERT( m_dwBufferSize == 0 );
|
|
|
|
m_dwBufferSize = sizeof( DRIVE_LAYOUT_INFORMATION ) + 20*sizeof(PARTITION_INFORMATION);
|
|
// Allocate space for the drive layout information
|
|
m_pBuffer = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc(0, m_dwBufferSize);
|
|
if( !m_pBuffer )
|
|
{
|
|
m_dwBufferSize = 0;
|
|
CloseHandle( hDisk );
|
|
AddError( strErrors, IDS_ERR_ALLOCATION, FALSE );
|
|
// There is nothing else to do so return
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Read the structure of the disk
|
|
BOOL bResult = DeviceIoControl(
|
|
hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0,
|
|
m_pBuffer, m_dwBufferSize, &ulBytes, NULL );
|
|
|
|
// If the buffer is not large enough reallocate it
|
|
while (!bResult && GetLastError() == ERROR_MORE_DATA)
|
|
{
|
|
m_dwBufferSize = ulBytes;
|
|
LocalFree( m_pBuffer );
|
|
m_pBuffer = (PDRIVE_LAYOUT_INFORMATION)(LocalAlloc(0, m_dwBufferSize ));
|
|
if (!m_pBuffer)
|
|
{
|
|
m_dwBufferSize = 0;
|
|
CloseHandle( hDisk );
|
|
AddError( strErrors, IDS_ERR_ALLOCATION, FALSE );
|
|
return FALSE;
|
|
}
|
|
BOOL bResult = DeviceIoControl(
|
|
hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0,
|
|
m_pBuffer, m_dwBufferSize, &ulBytes, NULL );
|
|
}
|
|
|
|
// If we still cannot get the drive layout then return false
|
|
if( !bResult )
|
|
{
|
|
AddError( strErrors, IDS_ERR_GET_DRIVE_LAYOUT, TRUE );
|
|
CloseHandle(hDisk);
|
|
return FALSE;
|
|
}
|
|
|
|
m_bLoaded = TRUE;
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Public methods
|
|
|
|
/*
|
|
Public method: SaveDiskInfo
|
|
|
|
Purpose: Save the disk layout on disk
|
|
|
|
Parameters: [OUT] CString& strErrors
|
|
All errors found during SaveDiskInfo are reported through this string
|
|
[OUT] BOOL& bMissingDisk
|
|
Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
|
|
This may notify the calling procedure that the search for installed disks is over
|
|
|
|
Return value: TRUE if the disk information is saved successfully
|
|
*/
|
|
|
|
BOOL CDiskMap::SaveDiskInfo(
|
|
CString& strErrors,
|
|
BOOL& bMissingDisk )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
strErrors = _T("");
|
|
bMissingDisk = FALSE;
|
|
|
|
|
|
CString strFileName;
|
|
strFileName.Format(_T("\\\\.\\PHYSICALDRIVE%lu"), m_dwDiskNumber);
|
|
|
|
// Try to open the disk
|
|
HANDLE hDisk = CreateFile(
|
|
strFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0 , INVALID_HANDLE_VALUE );
|
|
if( hDisk == INVALID_HANDLE_VALUE )
|
|
{
|
|
// This disk is not installed in the system
|
|
bMissingDisk = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG ulBytes;
|
|
|
|
BOOL bResult = DeviceIoControl(
|
|
hDisk, IOCTL_DISK_SET_DRIVE_LAYOUT, m_pBuffer,
|
|
sizeof(DRIVE_LAYOUT_INFORMATION) + m_pBuffer->PartitionCount*sizeof(PARTITION_INFORMATION),
|
|
NULL, 0, &ulBytes, NULL );
|
|
|
|
if( !bResult )
|
|
{
|
|
AddError( strErrors, IDS_ERR_SET_DRIVE_LAYOUT, TRUE );
|
|
CloseHandle( hDisk );
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle( hDisk );
|
|
m_bLoaded = TRUE;
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
|
|
/*
|
|
Public method: ReadPartitions
|
|
|
|
Purpose: Retrieve all non-container partitions on the disk and create CPhysicalPartitionData instances for
|
|
them
|
|
If the disk info is not loaded the method calls LoadDiskInfo first
|
|
|
|
Parameters: [OUT] CObArray& arrPartitions
|
|
Array of CPhysicalPartitionData containing all physical partitions found on the disk
|
|
[OUT] CString& strErrors
|
|
All errors found during ReadPhysicalPartitions are reported through this string
|
|
[OUT] BOOL& bMissingDisk
|
|
Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
|
|
This may notify the calling procedure that the search for installed disks is over
|
|
[IN] CItemData* pParentData
|
|
The items' parent data. It will fill the m_pParentData member of all new
|
|
CPhysicalPartititionData instances
|
|
|
|
Return value: TRUE if the the method ends successfully
|
|
*/
|
|
|
|
BOOL CDiskMap::ReadPartitions(
|
|
CObArray& arrPartitions,
|
|
CString& strErrors,
|
|
BOOL& bMissingDisk,
|
|
CItemData* pParentData /* = NULL */)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
arrPartitions.RemoveAll();
|
|
strErrors = _T("");
|
|
|
|
if( m_bLoaded )
|
|
bMissingDisk = FALSE;
|
|
else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
return FALSE;
|
|
|
|
// Read all recognized partitions on the disk
|
|
for( DWORD dwIndex = 0; dwIndex < m_pBuffer->PartitionCount; dwIndex++ )
|
|
{
|
|
// A partition that is not logical volume has the highest bit of field
|
|
// PartitionType 0
|
|
if( m_pBuffer->PartitionEntry[dwIndex].RecognizedPartition &&
|
|
!( m_pBuffer->PartitionEntry[dwIndex].PartitionType & 0x80 ) )
|
|
{
|
|
// Create the logical volume item data
|
|
CPhysicalPartitionData* pData = new CPhysicalPartitionData(
|
|
m_dwDiskNumber,
|
|
m_pBuffer->Signature,
|
|
&(m_pBuffer->PartitionEntry[dwIndex]),
|
|
( dwIndex < 4 ) ? PT_Primary : PT_InExtendedPartition,
|
|
pParentData,
|
|
TRUE);
|
|
CString strMemberErrors;
|
|
pData->ReadItemInfo( strMemberErrors );
|
|
strErrors += strMemberErrors;
|
|
// Insert the structure in the members' data array This array must be sorted by starting offset
|
|
for( int i = 0; i < arrPartitions.GetSize(); i++ )
|
|
{
|
|
if( pData->m_PartInfo.StartingOffset.QuadPart < ((CPhysicalPartitionData*)arrPartitions[i])->m_PartInfo.StartingOffset.QuadPart )
|
|
break;
|
|
}
|
|
arrPartitions.InsertAt( i, pData );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Public method: ReadFreeSpaces
|
|
|
|
Purpose: Retrieve all free spaces on the disk and create CFreeSpaceData instances for
|
|
them. A free space is a space not contained by a physical partition
|
|
If the disk info is not loaded the method calls LoadDiskInfo first
|
|
|
|
Parameters: [OUT] CObArray& arrFreeSpaces
|
|
Array of CFreeSpaceData containing all free spaces found on the disk
|
|
[OUT] CString& strErrors
|
|
All errors found during ReadFreeSpaces are reported through this string
|
|
[OUT] BOOL& bMissingDisk
|
|
Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
|
|
This may notify the calling procedure that the search for installed disks is over
|
|
[IN] CItemData* pParentData
|
|
The items' parent data. It will fill the m_pParentData member of all new
|
|
CFreeSpaceData instances
|
|
|
|
Return value: TRUE if the method ends successfully
|
|
*/
|
|
|
|
BOOL CDiskMap::ReadFreeSpaces(
|
|
CObArray& arrFreeSpaces,
|
|
CString& strErrors,
|
|
BOOL& bMissingDisk,
|
|
CItemData* pParentData /* = NULL */)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
arrFreeSpaces.RemoveAll();
|
|
strErrors = _T("");
|
|
|
|
if( m_bLoaded )
|
|
bMissingDisk = FALSE;
|
|
else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
return FALSE;
|
|
|
|
DWORD dwNextIndex;
|
|
|
|
GetExtendedPartitionFreeSpaces( 0, m_llDiskSize, 0, dwNextIndex, arrFreeSpaces, pParentData );
|
|
|
|
for( int i = 0; i < arrFreeSpaces.GetSize(); i++ )
|
|
((CFreeSpaceData*)(arrFreeSpaces[i]))->m_dwFreeSpaceNumber = i+1;
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Public method: ReadPartitionInformation
|
|
|
|
Purpose: Retrieves a partition information from the disk (m_dwDiskNumber) layout
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
Offset of the partition
|
|
[OUT] PARTITION_INFORMATION& partInfo
|
|
Structure to receive the partition info
|
|
[OUT] PARTITION_TYPE& wPartitionType
|
|
Partition type ( primary or partition in ectended partition )
|
|
[OUT] CString& strErrors
|
|
All errors found during ReadPartitionInformation are reported through this string
|
|
[OUT] BOOL& bMissingDisk
|
|
Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
|
|
This may notify the calling procedure that the search for installed disks is over
|
|
|
|
Return value: TRUE if the query succeeded
|
|
*/
|
|
|
|
BOOL CDiskMap::ReadPartitionInformation(
|
|
LONGLONG llPartStartOffset,
|
|
PARTITION_INFORMATION& partInfo,
|
|
PARTITION_TYPE& wPartitionType,
|
|
CString& strErrors,
|
|
BOOL& bMissingDisk )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
strErrors = _T("");
|
|
|
|
if( m_bLoaded )
|
|
bMissingDisk = FALSE;
|
|
else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
return FALSE;
|
|
|
|
// Check all recognized partitions on the disk
|
|
for( DWORD dwIndex = 0; dwIndex < m_pBuffer->PartitionCount; dwIndex++ )
|
|
{
|
|
if( ( m_pBuffer->PartitionEntry[dwIndex].RecognizedPartition ) &&
|
|
( m_pBuffer->PartitionEntry[dwIndex].StartingOffset.QuadPart == llPartStartOffset ) )
|
|
{
|
|
wPartitionType = ( dwIndex < 4 ) ? PT_Primary : PT_InExtendedPartition;
|
|
memcpy(&partInfo, &(m_pBuffer->PartitionEntry[dwIndex]), sizeof(PARTITION_INFORMATION) );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// The partition was not found
|
|
AddError( strErrors, IDS_ERR_PARTITION_NOT_FOUND, FALSE );
|
|
|
|
return FALSE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Public method: DeletePartition
|
|
|
|
Purpose: Deletes a partition of the disk ( m_dwDiskNumber )
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
Offset of the partition
|
|
|
|
Return value: TRUE if the deletion succeeded
|
|
*/
|
|
|
|
BOOL CDiskMap::DeletePartition(
|
|
LONGLONG llPartStartOffset )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
CString strErrors;
|
|
BOOL bMissingDisk;
|
|
|
|
if( !m_bLoaded )
|
|
{
|
|
if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Retrieve the indexes in the partition table of the partition itself and of its parent extended partition ( if any )
|
|
DWORD dwPartIndex, dwParentIndex, dwNextIndex;
|
|
if( !SearchForPartitionInExtendedPartition( llPartStartOffset, MAXDWORD, 0, dwNextIndex, dwPartIndex, dwParentIndex ) )
|
|
{
|
|
// The partition was not found
|
|
AfxMessageBox( IDS_ERR_PARTITION_NOT_FOUND, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
// Delete the partition from its parent table
|
|
DeletePartitionFromTable( dwPartIndex );
|
|
|
|
// Update / Delete the parent extended partition ( depends on what's left inside it )
|
|
if( dwParentIndex != MAXDWORD ) // The partition was a member of an extended partition
|
|
UpdateExtendedPartitionAfterMemberDeletion( dwParentIndex, ((DWORD)( dwPartIndex / 4 ))*4 );
|
|
|
|
// Time to save on the disk
|
|
if( !SaveDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Public method: DeleteExtendedPartition
|
|
|
|
Purpose: Delete an extended partition of the disk ( m_dwDiskNumber ).
|
|
The extended partition should not be contained by another extended partition
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
Offset of the extended partition
|
|
|
|
Return value: TRUE if the deletion succeeded
|
|
*/
|
|
|
|
BOOL CDiskMap::DeleteExtendedPartition(
|
|
LONGLONG llPartStartOffset )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
CString strErrors;
|
|
BOOL bMissingDisk;
|
|
|
|
if( !m_bLoaded )
|
|
{
|
|
if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// The real offset of the extended partition is one track before the offset of the free space
|
|
llPartStartOffset -= m_llTrackSize;
|
|
|
|
// Search the extended partition in the disk 4 slots table
|
|
DWORD dwMemberCount = ( 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount;
|
|
for( UINT i = 0; i < dwMemberCount; i++ )
|
|
{
|
|
PARTITION_INFORMATION* pPart = &(m_pBuffer->PartitionEntry[i]);
|
|
|
|
if( IsContainerPartition( pPart->PartitionType ) && ( pPart->StartingOffset.QuadPart == llPartStartOffset ) )
|
|
break;
|
|
}
|
|
|
|
if( i == dwMemberCount )
|
|
{
|
|
// Partition not found
|
|
AfxMessageBox( IDS_ERR_PARTITION_NOT_FOUND, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
DeletePartitionFromTable(i);
|
|
|
|
// Time to save on the disk
|
|
if( !SaveDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Public method: CreatePartition
|
|
|
|
Purpose: Create a partition on the disk ( m_dwDiskNumber )
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
Aproximative starting offset of the partition ( this might be modified due to
|
|
the creation of an extra container for this partition
|
|
[IN] LONGLONG llPartSize
|
|
Aproximative size of the partition ( this should be rounded-up to the next cylinder border )
|
|
[OUT] LONGLONG& llExactPartStartOffset
|
|
The exact starting offset of the partition
|
|
|
|
Return value: TRUE if the creation succeeded
|
|
*/
|
|
|
|
BOOL CDiskMap::CreatePartition(
|
|
LONGLONG llPartStartOffset,
|
|
LONGLONG llPartSize,
|
|
LONGLONG& llExactPartStartOffset)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
CString strErrors;
|
|
BOOL bMissingDisk;
|
|
|
|
if( !m_bLoaded )
|
|
{
|
|
if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
LONGLONG llPartEndOffset = GetCylinderBorderAfter( llPartStartOffset + llPartSize );
|
|
LONGLONG llPartTrueSize = llPartEndOffset - llPartStartOffset;
|
|
LONGLONG llFreeSpaceStartOffset;
|
|
LONGLONG llFreeSpaceEndOffset;
|
|
BOOL bAtBeginningOfExtendedPartition;
|
|
DWORD dwNewPartIndex;
|
|
|
|
if( !SearchForFreeSpaceInExtendedPartition( llPartStartOffset, llPartTrueSize, 0, m_llDiskSize, 0,
|
|
llFreeSpaceStartOffset, llFreeSpaceEndOffset,
|
|
bAtBeginningOfExtendedPartition, dwNewPartIndex) )
|
|
{
|
|
// The free space was not found
|
|
AfxMessageBox( IDS_ERR_FREE_SPACE_NOT_FOUND, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
// If the partition must be created inside a container and it can't be created at the beginning of
|
|
// this container then a new container must be created for it
|
|
BOOL bCreateContainer = ( ( dwNewPartIndex >= 4 ) && !bAtBeginningOfExtendedPartition );
|
|
|
|
if( !AddPartitionToTable( llPartStartOffset, llPartTrueSize, dwNewPartIndex, bCreateContainer, TRUE ) )
|
|
return FALSE;
|
|
|
|
if( bCreateContainer )
|
|
llExactPartStartOffset = llPartStartOffset + m_llTrackSize;
|
|
else
|
|
llExactPartStartOffset = llPartStartOffset;
|
|
|
|
// Time to save on the disk
|
|
if( !SaveDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
|
|
/*
|
|
Public method: CreateExtendedPartition
|
|
|
|
Purpose: Create an extended partition on the disk ( m_dwDiskNumber )
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
Offset of the partition
|
|
[IN] LONGLONG llPartSize
|
|
Estimate size of the partition ( this should be rounded-up to the next cylinder border )
|
|
[OUT] LONGLONG& llNewFreeSpaceOffset
|
|
The starting offset of the newly created free space inside the new empty extended partition
|
|
Usually it is the starting offset of the extended partition + a track size
|
|
|
|
|
|
Return value: TRUE if the creation succeeded
|
|
*/
|
|
|
|
BOOL CDiskMap::CreateExtendedPartition(
|
|
LONGLONG llPartStartOffset,
|
|
LONGLONG llPartSize,
|
|
LONGLONG& llNewFreeSpaceOffset )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_dwDiskNumber >= 0 );
|
|
|
|
CString strErrors;
|
|
BOOL bMissingDisk;
|
|
|
|
if( !m_bLoaded )
|
|
{
|
|
if( !LoadDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
LONGLONG llPartEndOffset = GetCylinderBorderAfter( llPartStartOffset + llPartSize );
|
|
LONGLONG llPartTrueSize = llPartEndOffset - llPartStartOffset;
|
|
LONGLONG llFreeSpaceStartOffset;
|
|
LONGLONG llFreeSpaceEndOffset;
|
|
BOOL bAtBeginningOfExtendedPartition;
|
|
DWORD dwNewPartIndex;
|
|
|
|
if( !SearchForFreeSpaceInExtendedPartition( llPartStartOffset, llPartTrueSize, 0, m_llDiskSize, 0,
|
|
llFreeSpaceStartOffset, llFreeSpaceEndOffset,
|
|
bAtBeginningOfExtendedPartition, dwNewPartIndex) ||
|
|
( dwNewPartIndex >= 4 ) )
|
|
{
|
|
// The free space was not found or was found in another extended partition
|
|
AfxMessageBox( IDS_ERR_FREE_SPACE_NOT_FOUND, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !AddPartitionToTable( llPartStartOffset, llPartTrueSize, dwNewPartIndex, TRUE, FALSE ) )
|
|
return FALSE;
|
|
|
|
// Time to save on the disk
|
|
if( !SaveDiskInfo( strErrors, bMissingDisk ) )
|
|
{
|
|
if( bMissingDisk )
|
|
strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
|
|
AfxMessageBox( strErrors, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
llNewFreeSpaceOffset = llPartStartOffset + m_llTrackSize;
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Protected methods
|
|
|
|
/*
|
|
Protected method: GetNextIndex
|
|
|
|
Purpose: Given an extended partition table index in m_pBuffer->PartitionInfo, scan recursively
|
|
the extended partition tree and find the chunk of m_pBuffer->PartitionInfo filled with
|
|
members of the extended partition
|
|
Then return the next index. This index is the index of the following extended partition
|
|
from the same ( or superior ) level as the given extended partition
|
|
|
|
Parameters: [IN] DWORD dwTableIndex
|
|
Index in m_pBuffer->PartitionInfo of the extended partition
|
|
( 0 for the whole disk );
|
|
|
|
Return value: Index in m_pBuffer->PartitionEntry of the next extended partition table on the same level
|
|
*/
|
|
|
|
DWORD CDiskMap::GetNextIndex( DWORD dwTableIndex )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( dwTableIndex >= 0 );
|
|
ASSERT( dwTableIndex%4 == 0 );
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
|
|
DWORD dwNextIndex = dwTableIndex + 4;
|
|
|
|
// 1. Scan the partition table. Compute the end offset for every member. For container members retrieve all
|
|
// their free spaces.
|
|
for( UINT i = 0; ( i < 4 ) && ( dwTableIndex + i < m_pBuffer->PartitionCount ); i++ )
|
|
{
|
|
if( IsContainerPartition( pTable[i].PartitionType ) )
|
|
dwNextIndex = GetNextIndex( dwNextIndex );
|
|
}
|
|
|
|
return dwNextIndex;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: GetExtendedPartitionFreeSpaces
|
|
|
|
Purpose: Search recursively for free spaces inside an extended partition given its starting offset,
|
|
size and partitions table index inside m_pBuffer
|
|
This method may be called also to get the free spaces of the whole disk.
|
|
|
|
For every found free space create a CFreeSpace instance and add it to arrFreeSpaces
|
|
|
|
Parameters: [IN] LONGLONG llExtPartStartOffset
|
|
The start offset of the extended partition ( 0 for the whole disk )
|
|
[IN] LONGLONG llExtPartEndOffset
|
|
The end offset of the extended partition ( m_llDiskSize for the whole disk )
|
|
[IN] DWORD dwTableIndex
|
|
Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
|
|
( 0 for the whole disk )
|
|
[OUT] DWORD& dwNextIndex
|
|
Index in m_pBuffer->PartitionEntry of the next extended partition table
|
|
[OUT] CObArray& arrFreeSpaces
|
|
Array of CFreeSpaceData containing all free spaces found in this extended partition (disk)
|
|
[IN] CItemData* pParentData
|
|
The items' parent data. It will fill the m_pParentData member of all new
|
|
CFreeSpaceData instances
|
|
|
|
Return value: -
|
|
*/
|
|
|
|
void CDiskMap::GetExtendedPartitionFreeSpaces(
|
|
LONGLONG llExtPartStartOffset,
|
|
LONGLONG llExtPartEndOffset,
|
|
DWORD dwTableIndex,
|
|
DWORD& dwNextIndex,
|
|
CObArray& arrFreeSpaces,
|
|
CItemData* pParentData /* = NULL */)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( dwTableIndex >= 0 );
|
|
ASSERT( dwTableIndex%4 == 0 );
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
CObArray arrPartFreeSpaces[4];
|
|
LONGLONG arrPartEndOffset[4];
|
|
PARTITION_INFORMATION* pPart;
|
|
|
|
DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
|
|
|
|
// 1. Order the members by starting offset ( they may be not ordered in pTable )
|
|
|
|
DWORD arrOrder[4];
|
|
DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
|
|
ASSERT( dwPartitionCount <= dwMemberCount );
|
|
|
|
// 2. Scan the partition table. For container members retrieve all their free spaces.
|
|
|
|
dwNextIndex = dwTableIndex + 4;
|
|
DWORD dwExtendedPartitionCountOnLevel = 0;
|
|
|
|
for( UINT i = 0; i < dwMemberCount; i++ )
|
|
{
|
|
pPart = &(pTable[i]);
|
|
if( pPart->PartitionType == 0 )
|
|
continue;
|
|
|
|
if( IsContainerPartition( pPart->PartitionType ) )
|
|
{
|
|
if( dwTableIndex == 0 )
|
|
{
|
|
// This is a root container partition. Its superior limit is given by its size
|
|
arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
// This is a container inside another container. Its superior limit is given by the starting offset
|
|
// of the next partition ( as position ) or by the end of its parent container
|
|
for( UINT j = 0; j < dwPartitionCount; j++ )
|
|
{
|
|
if( arrOrder[j] == i )
|
|
break;
|
|
}
|
|
ASSERT( j < dwPartitionCount );
|
|
if( j < dwPartitionCount - 1 )
|
|
arrPartEndOffset[i] = pTable[ arrOrder[j+1] ].StartingOffset.QuadPart;
|
|
else
|
|
arrPartEndOffset[i] = llExtPartEndOffset;
|
|
}
|
|
|
|
GetExtendedPartitionFreeSpaces(
|
|
pPart->StartingOffset.QuadPart, arrPartEndOffset[i],
|
|
dwNextIndex, dwNextIndex, arrPartFreeSpaces[i], pParentData );
|
|
|
|
dwExtendedPartitionCountOnLevel++;
|
|
}
|
|
else
|
|
arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
|
|
}
|
|
|
|
// 3. Now retrieve all free spaces inside this extended partition and add them to arrFreeSpaces
|
|
// together with the free spaces of the container members. arrFreeSpaces must be sorted by starting offset
|
|
|
|
// The first track of the disk / extended partition is reserved
|
|
LONGLONG llFreeSpaceStartOffset = llExtPartStartOffset + m_llTrackSize;
|
|
LONGLONG llFreeSpaceEndOffset;
|
|
for( i = 0; i <= dwPartitionCount ; i++ )
|
|
{
|
|
// The end of a free space could be the starting offset of the next member or the end of the extended
|
|
// partition
|
|
if( i == dwPartitionCount )
|
|
llFreeSpaceEndOffset = llExtPartEndOffset;
|
|
else
|
|
{
|
|
pPart = &(pTable[ arrOrder[i] ]);
|
|
// Always take the greatest cylinder border less than or equal with the starting offset of the next member
|
|
llFreeSpaceEndOffset = GetCylinderBorderBefore(pPart->StartingOffset.QuadPart);
|
|
}
|
|
|
|
// A free space must be at least 1 cylinder size long
|
|
if( llFreeSpaceStartOffset + m_llCylinderSize <= llFreeSpaceEndOffset )
|
|
{
|
|
FREE_SPACE_TYPE wFreeSpaceType;
|
|
if( dwTableIndex == 0 ) // This is a free space between primary partitions
|
|
wFreeSpaceType = FST_Primary;
|
|
else if( dwPartitionCount > 0 ) // This is a free space inside a non-empty extended partition
|
|
wFreeSpaceType = FST_InExtendedPartition;
|
|
else // This is an empty extended partition
|
|
wFreeSpaceType = FST_EmptyExtendedPartition;
|
|
|
|
// We found a free space !!!
|
|
CFreeSpaceData* pData = new CFreeSpaceData(
|
|
m_dwDiskNumber,
|
|
m_pBuffer->Signature,
|
|
llFreeSpaceStartOffset,
|
|
llFreeSpaceEndOffset - llFreeSpaceStartOffset,
|
|
wFreeSpaceType,
|
|
m_llCylinderSize,
|
|
dwPartitionCount - dwExtendedPartitionCountOnLevel,
|
|
dwExtendedPartitionCountOnLevel,
|
|
pParentData
|
|
);
|
|
CString strMemberErrors;
|
|
pData->ReadItemInfo( strMemberErrors );
|
|
ASSERT( strMemberErrors.IsEmpty() );
|
|
// Add the structure to the members' data array
|
|
arrFreeSpaces.Add(pData);
|
|
}
|
|
|
|
if( i != dwPartitionCount )
|
|
{
|
|
// The starting offset of the next free space could be the end offset of the current member
|
|
// Always take the lower cylinder border greater or equal with this value
|
|
llFreeSpaceStartOffset = GetCylinderBorderAfter( arrPartEndOffset[ arrOrder[i] ] );
|
|
if( IsContainerPartition( pPart->PartitionType ) )
|
|
arrFreeSpaces.Append( arrPartFreeSpaces[ arrOrder[i] ] );
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: SortPartitionsByOffset
|
|
|
|
Purpose: Sort a partition table by starting offset without actually changing the table
|
|
|
|
Parameters: [IN] PARTITION_INFORMATION* arrTable
|
|
Array of partitions ( inside m_pBuffer )
|
|
[IN] DWORD
|
|
The size of the array
|
|
[OUT] DWORD* arrOrder
|
|
Array of indexes in arrTable of partitions sorted by starting offset
|
|
The null partitions are not considered at all
|
|
|
|
Return value: The number of not null sorted partitions
|
|
*/
|
|
|
|
DWORD CDiskMap::SortPartitionsByOffset(
|
|
PARTITION_INFORMATION* arrTable,
|
|
DWORD dwSize,
|
|
DWORD* arrOrder )
|
|
{
|
|
MY_TRY
|
|
|
|
DWORD dwOrderedSize = 0;
|
|
for( DWORD i = 0; i < dwSize; i++ )
|
|
{
|
|
// Just ignore null partitions
|
|
if( arrTable[i].PartitionType == 0 )
|
|
continue;
|
|
|
|
for( DWORD j = 0; j < dwOrderedSize; j++ )
|
|
{
|
|
if( arrTable[i].StartingOffset.QuadPart < arrTable[arrOrder[j]].StartingOffset.QuadPart )
|
|
break;
|
|
}
|
|
|
|
for( DWORD k = dwOrderedSize; k > j; k-- )
|
|
arrOrder[k] = arrOrder[k-1];
|
|
|
|
arrOrder[j] = i;
|
|
dwOrderedSize++;
|
|
}
|
|
return dwOrderedSize;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: GetCylinderBorderBefore
|
|
|
|
Purpose: Get the greatest cylinder border less than or equal with an offset
|
|
|
|
Parameters: [IN] LONGLONG llOffset
|
|
The offset
|
|
|
|
Return value: The greatest cylinder border less than or equal with llOffset
|
|
*/
|
|
|
|
LONGLONG CDiskMap::GetCylinderBorderBefore( LONGLONG llOffset )
|
|
{
|
|
// This is the most common case so treat it separately
|
|
if( llOffset%m_llCylinderSize == 0 )
|
|
return llOffset;
|
|
|
|
//And this the generic formula
|
|
return ((LONGLONG)(llOffset / m_llCylinderSize)) * m_llCylinderSize;
|
|
}
|
|
|
|
/*
|
|
Protected method: GetCylinderBorderAfter
|
|
|
|
Purpose: Get the lowest cylinder border greater than or equal with an offset
|
|
|
|
Parameters: [IN] LONGLONG llOffset
|
|
The offset
|
|
|
|
Return value: The lowest cylinder border greater than or equal with llOffset
|
|
*/
|
|
|
|
LONGLONG CDiskMap::GetCylinderBorderAfter( LONGLONG llOffset )
|
|
{
|
|
// This is the most common case so treat it separately
|
|
if( llOffset%m_llCylinderSize == 0 )
|
|
return llOffset;
|
|
|
|
// And this is the generic formula
|
|
return ((LONGLONG)((llOffset + m_llCylinderSize - 1) / m_llCylinderSize)) * m_llCylinderSize;
|
|
}
|
|
|
|
/*
|
|
Protected method: SearchForPartitionInExtendedPartition
|
|
|
|
Purpose: Search recursively for a partition inside an extended partition
|
|
This method may be called also to search for a partition inside the whole disk.
|
|
|
|
Parameters: [IN] LONGLONG llPartOffset
|
|
The start offset of the partition we are looking for
|
|
[IN] DWORD dwExtPartIndex
|
|
Index in m_pBuffer->PartitionEntry of the extended partition
|
|
( MAXDWORD for the whole disk because we don't have -1 available )
|
|
[IN] DWORD dwTableIndex
|
|
Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
|
|
( 0 for the whole disk )
|
|
[OUT] DWORD& dwNextIndex
|
|
Index in m_pBuffer->PartitionEntry of the next extended partition table
|
|
[OUT] DWORD& dwPartIndex
|
|
Index in m_pBuffer->PartitionEntry of the partition we are looking for ( if found )
|
|
[OUT] DWORD& dwParentIndex
|
|
Index in m_pBuffer->PartitionEntry of the parent partition ( if partition found )
|
|
|
|
Return value: TRUE if the partition was found inside this extended partition ( disk )
|
|
*/
|
|
|
|
BOOL CDiskMap::SearchForPartitionInExtendedPartition(
|
|
LONGLONG llPartOffset,
|
|
DWORD dwExtPartIndex,
|
|
DWORD dwTableIndex,
|
|
DWORD& dwNextIndex,
|
|
DWORD& dwPartIndex,
|
|
DWORD& dwParentIndex )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( dwTableIndex >= 0 );
|
|
ASSERT( dwTableIndex%4 == 0 );
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
|
|
dwNextIndex = dwTableIndex + 4;
|
|
|
|
for( UINT i = 0; ( i < 4 ) && ( dwTableIndex + i < m_pBuffer->PartitionCount ) ; i++ )
|
|
{
|
|
PARTITION_INFORMATION* pPart = &(pTable[i]);
|
|
if( pPart->PartitionType == 0 )
|
|
continue;
|
|
|
|
if( IsContainerPartition( pPart->PartitionType ) )
|
|
{
|
|
// Search in depth
|
|
if( SearchForPartitionInExtendedPartition( llPartOffset, dwTableIndex + i, dwNextIndex,
|
|
dwNextIndex, dwPartIndex, dwParentIndex ) )
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if( pPart->StartingOffset.QuadPart == llPartOffset )
|
|
{
|
|
// This is my partition
|
|
dwPartIndex = dwTableIndex + i;
|
|
dwParentIndex = dwExtPartIndex;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: DeletePartitionFromTable
|
|
|
|
Purpose: Delete a partition from m_pBuffer->PartitionInfo table.
|
|
|
|
Parameters: [IN] DWORD dwPartIndex
|
|
The index of the partition in m_pBuffer->PartitionInfo
|
|
|
|
|
|
Return value: -
|
|
*/
|
|
|
|
void CDiskMap::DeletePartitionFromTable( DWORD dwPartIndex )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( ( dwPartIndex >= 0 ) && ( dwPartIndex < m_pBuffer->PartitionCount ) );
|
|
|
|
// Get the 4 slots table of its parent
|
|
DWORD dwTableIndex = ((DWORD)( dwPartIndex / 4 )) * 4;
|
|
DWORD dwEndIndex = dwTableIndex + 3;
|
|
if( dwEndIndex >= m_pBuffer->PartitionCount )
|
|
dwEndIndex = m_pBuffer->PartitionCount - 1;
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
|
|
// Check if the given partition is a container partition
|
|
BOOL bContainer = IsContainerPartition( m_pBuffer->PartitionEntry[dwPartIndex].PartitionType );
|
|
|
|
// If container partition get the index in m_pBuffer->PartitionEntry for the 4 slots table of that partition
|
|
DWORD dwPartTableIndex = dwTableIndex + 4;
|
|
DWORD dwPartNextIndex;
|
|
DWORD dwShift = 0;
|
|
if( bContainer )
|
|
{
|
|
for( DWORD i = dwTableIndex; i < dwPartIndex; i++ )
|
|
{
|
|
if( IsContainerPartition( m_pBuffer->PartitionEntry[i].PartitionType ) )
|
|
dwPartTableIndex = GetNextIndex( dwPartTableIndex );
|
|
}
|
|
|
|
dwPartNextIndex = GetNextIndex( dwPartTableIndex );
|
|
dwShift = dwPartNextIndex - dwPartTableIndex;
|
|
}
|
|
|
|
// Move all following partitions one position to the left in the 4 slots table
|
|
for( DWORD i = dwPartIndex; i < dwEndIndex; i++ )
|
|
{
|
|
memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+1]), sizeof( PARTITION_INFORMATION ) );
|
|
m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
|
|
}
|
|
|
|
|
|
// Fill the last entry in the 4 slots table with zero
|
|
memset( &(m_pBuffer->PartitionEntry[ dwEndIndex ]), 0, sizeof( PARTITION_INFORMATION ) );
|
|
m_pBuffer->PartitionEntry[dwEndIndex].RewritePartition = TRUE;
|
|
|
|
// If container partition remove from m_pBuffer->PartitionEntry all entries belonging to this container partition
|
|
if( bContainer && ( dwShift > 0 ) )
|
|
{
|
|
for( DWORD i = dwPartTableIndex; i < m_pBuffer->PartitionCount - dwShift; i++ )
|
|
{
|
|
memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+dwShift]), sizeof( PARTITION_INFORMATION ) );
|
|
m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
|
|
}
|
|
m_pBuffer->PartitionCount -= dwShift;
|
|
}
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: UpdateExtendedPartitionAfterMemberDeletion
|
|
|
|
Purpose: Update / Delete an extended partition after one of its members was deleted
|
|
If the extended partition remains empty then it must be deleted
|
|
If only one member is left and this member is an extended partition too then replace the
|
|
extended partition with its member ( promote the member on the superior level )
|
|
All other situations are unlikely to happen so I don't treat them here
|
|
|
|
Parameters: [IN] DWORD dwPartIndex
|
|
The index in m_pBuffer->PartitionInfo of the partition
|
|
[IN] DWORD
|
|
The index in m_pBuffer->PartitionInfo of the 4 slots table of the partition
|
|
|
|
Return value: -
|
|
*/
|
|
|
|
void CDiskMap::UpdateExtendedPartitionAfterMemberDeletion(
|
|
DWORD dwPartIndex,
|
|
DWORD dwTableIndex )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( ( dwPartIndex >= 0 ) && ( dwPartIndex < m_pBuffer->PartitionCount ) );
|
|
ASSERT( dwTableIndex >= 0 );
|
|
ASSERT( dwTableIndex%4 == 0 );
|
|
|
|
// If the extended partition is member of the whole disk then don't touch it
|
|
if( dwPartIndex < 4 )
|
|
return;
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
|
|
// Order the members by starting offset
|
|
DWORD arrOrder[4];
|
|
DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
|
|
dwMemberCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
|
|
|
|
if( dwMemberCount == 0 )
|
|
{
|
|
// The extended partition is empty so delete it
|
|
DeletePartitionFromTable( dwPartIndex );
|
|
return;
|
|
}
|
|
else if( dwMemberCount == 1 )
|
|
{
|
|
PARTITION_INFORMATION* pPart = &(pTable[ arrOrder[0] ]);
|
|
if( IsContainerPartition( pPart->PartitionType ) )
|
|
{
|
|
// Replace the parent extended partition with this extended partition
|
|
memcpy( &(m_pBuffer->PartitionEntry[dwPartIndex]), pPart, sizeof(PARTITION_INFORMATION) );
|
|
m_pBuffer->PartitionEntry[dwPartIndex].RewritePartition = TRUE;
|
|
// Remove the 4 slots table of the parent extended partition
|
|
for( DWORD i = dwTableIndex; ( i + 4 < m_pBuffer->PartitionCount ); i++ )
|
|
{
|
|
memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+4]), sizeof( PARTITION_INFORMATION ) );
|
|
m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
|
|
}
|
|
m_pBuffer->PartitionCount -= 4;
|
|
return;
|
|
}
|
|
}
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: SearchForFreeSpaceInExtendedPartition
|
|
|
|
Purpose: Given an offset and size search recursively for an appropriate free space
|
|
inside an extended partition ( or the whole disk ).
|
|
Return the start and end offset of the whole free space and the index in the partition table
|
|
where a new partition having the given offset and size might be inserted.
|
|
|
|
Parameters: [IN] LONGLONG llOffset
|
|
The start offset of the space we are looking for
|
|
[IN] LONGLONG llSize
|
|
The size of the space
|
|
[IN] LONGLONG llExtPartStartOffset
|
|
The start offset of the extended partition ( 0 for the whole disk )
|
|
[IN] LONGLONG llExtPartEndOffset
|
|
The end offset of the extended partition ( m_llDiskSize for the whole disk )
|
|
[IN] DWORD dwTableIndex
|
|
Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
|
|
( 0 for the whole disk )
|
|
[OUT] LONGLONG& llFreeSpaceStartOffset
|
|
The start offset of the appropriate free space
|
|
[OUT] LONGLONG& llFreeSpaceEndOffset
|
|
The end offset of the appropriate end space
|
|
[OUT] BOOL &bAtBeginningOfExtendedPartition
|
|
Is the free space at the beginning of the extended partition?
|
|
[OUT] DWORD& dwNewPartIndex
|
|
The index in m_pBuffer->PartitionEntry where a new partition having the given offset
|
|
and size may be inserted
|
|
|
|
|
|
Return value: TRUE if a free space was found
|
|
*/
|
|
|
|
BOOL CDiskMap::SearchForFreeSpaceInExtendedPartition(
|
|
LONGLONG llOffset,
|
|
LONGLONG llSize,
|
|
LONGLONG llExtPartStartOffset,
|
|
LONGLONG llExtPartEndOffset,
|
|
DWORD dwTableIndex,
|
|
LONGLONG& llFreeSpaceStartOffset,
|
|
LONGLONG& llFreeSpaceEndOffset,
|
|
BOOL &bAtBeginningOfExtendedPartition,
|
|
DWORD& dwNewPartIndex)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( dwTableIndex >= 0 );
|
|
ASSERT( dwTableIndex%4 == 0 );
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
LONGLONG arrPartEndOffset[4];
|
|
PARTITION_INFORMATION* pPart;
|
|
|
|
DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
|
|
|
|
// 1. Order the members by starting offset ( they may be not ordered in pTable )
|
|
|
|
DWORD arrOrder[4];
|
|
DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
|
|
ASSERT( dwPartitionCount <= dwMemberCount );
|
|
|
|
// 2. Scan the partition table. For container members try to create the partition inside them
|
|
|
|
DWORD dwNextIndex = dwTableIndex + 4;
|
|
|
|
for( UINT i = 0; i < dwMemberCount; i++ )
|
|
{
|
|
pPart = &(pTable[i]);
|
|
if( pPart->PartitionType == 0 )
|
|
continue;
|
|
|
|
if( IsContainerPartition( pPart->PartitionType ) )
|
|
{
|
|
if( dwTableIndex == 0 )
|
|
{
|
|
// This is a root container partition. Its superior limit is given by its size
|
|
arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
// This is a container inside another container. Its superior limit is given by the starting offset
|
|
// of the next partition ( as position ) or by the end of its parent container
|
|
for( UINT j = 0; j < dwPartitionCount; j++ )
|
|
{
|
|
if( arrOrder[j] == i )
|
|
break;
|
|
}
|
|
ASSERT( j < dwPartitionCount );
|
|
if( j < dwPartitionCount - 1 )
|
|
arrPartEndOffset[i] = pTable[ arrOrder[j+1] ].StartingOffset.QuadPart;
|
|
else
|
|
arrPartEndOffset[i] = llExtPartEndOffset;
|
|
}
|
|
|
|
if( ( llOffset >= pPart->StartingOffset.QuadPart ) &&
|
|
( llOffset + llSize <= arrPartEndOffset[i] ) )
|
|
{
|
|
// Create the partition inside this container
|
|
return SearchForFreeSpaceInExtendedPartition( llOffset, llSize,
|
|
pPart->StartingOffset.QuadPart, arrPartEndOffset[i], dwNextIndex,
|
|
llFreeSpaceStartOffset, llFreeSpaceEndOffset, bAtBeginningOfExtendedPartition, dwNewPartIndex );
|
|
}
|
|
else
|
|
dwNextIndex = GetNextIndex(dwNextIndex);
|
|
}
|
|
else
|
|
arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
|
|
}
|
|
|
|
// 3. We must search for our free space among the free spaces between the members of this extended partition !!!
|
|
|
|
// The first track of the disk / extended partition is reserved
|
|
llFreeSpaceStartOffset = llExtPartStartOffset + m_llTrackSize;
|
|
|
|
for( i = 0 ; i <= dwPartitionCount ; i++ )
|
|
{
|
|
// The end of a free space could be the starting offset of the next member or the end of the extended
|
|
// partition
|
|
if( i == dwPartitionCount )
|
|
llFreeSpaceEndOffset = llExtPartEndOffset;
|
|
else
|
|
{
|
|
pPart = &(pTable[ arrOrder[i] ]);
|
|
// Always take the greatest cylinder border less than or equal with the starting offset of the next member
|
|
llFreeSpaceEndOffset = GetCylinderBorderBefore(pPart->StartingOffset.QuadPart);
|
|
}
|
|
|
|
// A free space must be at least 1 cylinder size long
|
|
// Check also if the new partition would match in the free space
|
|
if( ( llFreeSpaceStartOffset + m_llCylinderSize <= llFreeSpaceEndOffset ) &&
|
|
( llOffset >= llFreeSpaceStartOffset) &&
|
|
( llOffset + llSize <= llFreeSpaceEndOffset) )
|
|
{
|
|
// We found the appropriate free space but there is still a problem
|
|
// What if the 4 slots table is already full?
|
|
|
|
if( dwPartitionCount >= dwMemberCount )
|
|
{
|
|
AfxMessageBox( IDS_ERR_PARTITION_TABLE_FULL, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
bAtBeginningOfExtendedPartition = ( llFreeSpaceStartOffset == llExtPartStartOffset + m_llTrackSize );
|
|
dwNewPartIndex = dwTableIndex + i;
|
|
return TRUE;
|
|
}
|
|
|
|
if( i != dwPartitionCount )
|
|
{
|
|
// The starting offset of the next free space could be the end offset of the current member
|
|
// Always take the lower cylinder border greater or equal with this value
|
|
llFreeSpaceStartOffset = GetCylinderBorderAfter( arrPartEndOffset[ arrOrder[i] ] );
|
|
}
|
|
}
|
|
|
|
// We didn't find a matching free space
|
|
|
|
return FALSE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: AddPartitionToTable
|
|
|
|
Purpose: Add a new container partition AND/OR a new non-container partition to m_pBuffer->PartitionInfo
|
|
table and make all necessary changes to the partition table
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
The starting offset of the new partition
|
|
[IN] LONGLONG llPartTrueSize
|
|
The size of the new partition
|
|
[IN] DWORD dwNewPartIndex
|
|
The index of the new partition in m_pBuffer->PartitionInfo
|
|
[IN] BOOL bCreateContainer
|
|
Should we create a new extended partition?
|
|
[IN] BOOL bCreateNonContainer
|
|
Should we create a new non-container partition?
|
|
|
|
Note: bCreateContainer and bNonCreateContainer can be both TRUE. Then a container partition
|
|
is created first and a non-container partition is created inside this container
|
|
These BOOL values cannot be both FALSE.
|
|
|
|
|
|
Return value: TRUE if the partition table was modified successfully
|
|
*/
|
|
|
|
BOOL CDiskMap::AddPartitionToTable(
|
|
LONGLONG llPartStartOffset,
|
|
LONGLONG llPartSize,
|
|
DWORD dwNewPartIndex,
|
|
BOOL bCreateContainer,
|
|
BOOL bCreateNonContainer)
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( ( dwNewPartIndex >= 0 ) && ( dwNewPartIndex < m_pBuffer->PartitionCount ) );
|
|
|
|
// We can create a new container partition, a new non-container partition or a
|
|
// new non-container partition inside a new container partition
|
|
ASSERT( bCreateContainer || bCreateNonContainer );
|
|
|
|
// Get the 4 slots table of its parent
|
|
DWORD dwTableIndex = ((DWORD)( dwNewPartIndex / 4 )) * 4;
|
|
DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
|
|
|
|
PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
|
|
|
|
// 1. Get the chunks of m_pBuffer->PartitionEntry allocated for every container member of the extended partition
|
|
DWORD arrStartIndex[4];
|
|
DWORD arrEndIndex[4];
|
|
DWORD dwNextIndex = dwTableIndex + 4;
|
|
|
|
for( DWORD i = 0; i < dwMemberCount; i++ )
|
|
{
|
|
if( IsContainerPartition( pTable[i].PartitionType ) )
|
|
{
|
|
arrStartIndex[i] = dwNextIndex;
|
|
arrEndIndex[i] = dwNextIndex = GetNextIndex(dwNextIndex);
|
|
}
|
|
}
|
|
|
|
// 2. Sort the members of the extended partition by starting offset
|
|
DWORD arrOrder[4];
|
|
DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
|
|
|
|
// There should be another free slot in the table. SearchForFreeSpaceInExtendedPartition should take care of this
|
|
ASSERT( dwPartitionCount < dwMemberCount );
|
|
|
|
|
|
// 3. Allocate a new buffer. We must be prepared to add some extra 4 slots to the partition table
|
|
PDRIVE_LAYOUT_INFORMATION pNewBuffer = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc( 0,
|
|
sizeof(DRIVE_LAYOUT_INFORMATION) + ( m_pBuffer->PartitionCount + 4 )*sizeof(PARTITION_INFORMATION) );
|
|
if( pNewBuffer == NULL )
|
|
{
|
|
AfxMessageBox( IDS_ERR_ALLOCATION, MB_ICONSTOP);
|
|
return FALSE;
|
|
}
|
|
|
|
// The drive info and the first m_dwTableIndex partition entries will not be changed in the new buffer
|
|
memcpy( pNewBuffer, m_pBuffer, sizeof(DRIVE_LAYOUT_INFORMATION) + dwTableIndex*sizeof(PARTITION_INFORMATION) );
|
|
|
|
// 4. Add the first ( dwNewPartIndex - dwTableIndex ) members of the extended partition to the new buffer
|
|
// in the starting offset order. Add also their chunks of m_pBuffer->PartitionEntry
|
|
|
|
PARTITION_INFORMATION* pNewTable = &( pNewBuffer->PartitionEntry[dwTableIndex] );
|
|
UINT iNext; // Index in pTable
|
|
UINT iNewNext = 0; // Index in pNewTable
|
|
DWORD dwNewNextIndex = dwTableIndex + 4; // Index in pNewBuffer->PartitionEntry of the next free chunk for an extended partition
|
|
|
|
for( iNext = 0, iNewNext = 0; iNext < dwNewPartIndex - dwTableIndex; iNext++, iNewNext++ )
|
|
{
|
|
DWORD iOrder = arrOrder[iNext];
|
|
memcpy( &(pNewTable[iNewNext]), &(pTable[iOrder]), sizeof(PARTITION_INFORMATION));
|
|
if( iNewNext != iOrder )
|
|
pNewTable[iNewNext].RewritePartition = TRUE;
|
|
|
|
if( IsContainerPartition( pNewTable[iNewNext].PartitionType ) )
|
|
{
|
|
memcpy( &(pNewBuffer->PartitionEntry[dwNewNextIndex]),
|
|
&(m_pBuffer->PartitionEntry[ arrStartIndex[iOrder] ]),
|
|
( arrEndIndex[iOrder] - arrStartIndex[iOrder] ) * sizeof(PARTITION_INFORMATION));
|
|
|
|
if( arrStartIndex[iOrder] != dwNewNextIndex )
|
|
{
|
|
for( DWORD j = arrStartIndex[iOrder]; j < arrEndIndex[iOrder]; j++ )
|
|
pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
|
|
}
|
|
else
|
|
dwNewNextIndex += ( arrEndIndex[iOrder] - arrStartIndex[iOrder] );
|
|
}
|
|
}
|
|
|
|
// 5. Add the partition ( or a container if required ) in the slot dwNewPartIndex
|
|
FillNewPartitionInfo( llPartStartOffset, llPartSize,
|
|
&(pNewTable[iNewNext++]), bCreateContainer );
|
|
if( bCreateContainer )
|
|
{
|
|
// 5.1 Add a new 4 slots table for the new container
|
|
pNewBuffer->PartitionCount += 4;
|
|
PARTITION_INFORMATION* pContainerTable = &( pNewBuffer->PartitionEntry[dwNewNextIndex] );
|
|
dwNewNextIndex += 4;
|
|
UINT iContainerNext = 0;
|
|
|
|
// 5.2 Create the non-container partition in the first slot
|
|
if( bCreateNonContainer )
|
|
FillNewPartitionInfo( llPartStartOffset + m_llTrackSize, llPartSize - m_llTrackSize,
|
|
&(pContainerTable[iContainerNext++]), FALSE );
|
|
|
|
if( dwTableIndex == 0 )
|
|
{
|
|
// The new container is among primary partitions
|
|
|
|
// 5.3 Fill with zeroes the last entries of the new container table
|
|
for( ; iContainerNext < 4; iContainerNext++ )
|
|
{
|
|
memset( &(pContainerTable[iContainerNext]), 0, sizeof(PARTITION_INFORMATION) );
|
|
pContainerTable[iContainerNext].RewritePartition = TRUE;
|
|
}
|
|
// All following primary partitions will be added to pNewTable after this new container
|
|
}
|
|
else
|
|
{
|
|
// The new container is inside another container
|
|
|
|
// 5.3 Fill with zeroes the last entries of the table pNewTable
|
|
for( ; iNewNext < 4; iNewNext++ )
|
|
{
|
|
memset( &(pNewTable[iNewNext]), 0, sizeof(PARTITION_INFORMATION));
|
|
pNewTable[iNewNext].RewritePartition = TRUE;
|
|
}
|
|
|
|
// 5.4. The new table becomes the table of the newly created container
|
|
// All following partitions of the parent container will be added to the new container
|
|
pNewTable = pContainerTable;
|
|
iNewNext = iContainerNext;
|
|
|
|
}
|
|
}
|
|
|
|
// 6. Copy the rest of valid partitions from pTable to pNewTable ( also ordered by starting offset )
|
|
for( ; iNext < dwPartitionCount; iNext++, iNewNext++ )
|
|
{
|
|
DWORD iOrder = arrOrder[iNext];
|
|
memcpy( &(pNewTable[iNewNext]), &(pTable[iOrder]), sizeof(PARTITION_INFORMATION));
|
|
pNewTable[iNewNext].RewritePartition = TRUE;
|
|
|
|
if( IsContainerPartition( pNewTable[iNewNext].PartitionType ) )
|
|
{
|
|
memcpy( &(pNewBuffer->PartitionEntry[dwNewNextIndex]),
|
|
&(m_pBuffer->PartitionEntry[ arrStartIndex[iOrder] ]),
|
|
( arrEndIndex[iOrder] - arrStartIndex[iOrder] ) * sizeof(PARTITION_INFORMATION));
|
|
|
|
if( arrStartIndex[iOrder] != dwNewNextIndex )
|
|
{
|
|
for( DWORD j = arrStartIndex[iOrder]; j < arrEndIndex[iOrder]; j++ )
|
|
pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
|
|
}
|
|
else
|
|
dwNewNextIndex += ( arrEndIndex[iOrder] - arrStartIndex[iOrder] );
|
|
}
|
|
}
|
|
|
|
// 7. Fill with zeroes the last entries of pNewTable
|
|
for( ; iNewNext < 4; iNewNext++ )
|
|
{
|
|
memset( &(pNewTable[iNewNext]), 0, sizeof(PARTITION_INFORMATION));
|
|
pNewTable[iNewNext].RewritePartition = TRUE;
|
|
}
|
|
|
|
// 8. Add the tail of m_pBuffer to the tail of pNewBuffer
|
|
if( bCreateContainer )
|
|
ASSERT( dwNewNextIndex == dwNextIndex + 4 );
|
|
else
|
|
ASSERT( dwNewNextIndex == dwNextIndex );
|
|
|
|
memcpy( &( pNewBuffer->PartitionEntry[dwNewNextIndex] ),
|
|
&( m_pBuffer->PartitionEntry[dwNextIndex] ),
|
|
( m_pBuffer->PartitionCount - dwNextIndex ) * sizeof(PARTITION_INFORMATION) );
|
|
|
|
if( dwNewNextIndex != dwNextIndex )
|
|
{
|
|
for( DWORD j = dwNextIndex; j < m_pBuffer->PartitionCount; j++ )
|
|
pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
|
|
}
|
|
else
|
|
dwNewNextIndex += ( m_pBuffer->PartitionCount - dwNextIndex );
|
|
ASSERT( dwNewNextIndex == pNewBuffer->PartitionCount );
|
|
|
|
// 9. Replace the old m_pBuffer with the new buffer
|
|
|
|
LocalFree( m_pBuffer );
|
|
m_pBuffer = pNewBuffer;
|
|
|
|
return TRUE;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: FillNewPartitionInfo
|
|
|
|
Purpose: Fill a PARTITION_INFORMATION structure with the info of a new partition
|
|
|
|
Parameters: [IN] LONGLONG llPartStartOffset
|
|
The starting offset of the new partition
|
|
[IN] LONGLONG llPartSize
|
|
The size of the new partition
|
|
[OUT] PARTITION_INFORMATION*
|
|
Pointer to the structure to fill
|
|
[IN] BOOL bContainer
|
|
Is the new partition a container?
|
|
|
|
|
|
Return value: -
|
|
*/
|
|
|
|
void CDiskMap::FillNewPartitionInfo( LONGLONG llPartStartOffset, LONGLONG llPartSize,/* LONGLONG llExtPartStartOffset, */
|
|
PARTITION_INFORMATION* pPartInfo, BOOL bContainer )
|
|
{
|
|
MY_TRY
|
|
|
|
ASSERT( m_bLoaded );
|
|
ASSERT( pPartInfo );
|
|
|
|
pPartInfo->StartingOffset.QuadPart = llPartStartOffset;
|
|
pPartInfo->PartitionLength.QuadPart = llPartSize;
|
|
|
|
//#pragma message("TODO: HiddenSectors must be the number of sectors between the start of the disk or extended partition that contains the new partition and the start of the new partition?")
|
|
pPartInfo->HiddenSectors = (DWORD)( ( llPartStartOffset ) / m_llSectorSize);
|
|
|
|
// Don't assign any particular number for the partition. The system will take care of that
|
|
pPartInfo->PartitionNumber = 0;
|
|
|
|
pPartInfo->BootIndicator = FALSE;
|
|
pPartInfo->RewritePartition = TRUE;
|
|
|
|
if( bContainer )
|
|
{
|
|
pPartInfo->PartitionType = PARTITION_EXTENDED;
|
|
pPartInfo->RecognizedPartition = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//Windisk and Disk Management don't care about the number of sectors of the partition
|
|
//They always create partitions with the type PARTITION_HUGE
|
|
|
|
/*
|
|
LONGLONG llSectorCount = ( llPartSize + m_llSectorSize - 1 ) / m_llSectorSize;
|
|
if( llSectorCount <= CSEC_FAT )
|
|
pPartInfo->PartitionType = PARTITION_FAT_12;
|
|
else if( llSectorCount <= CSEC_FAT32MEG )
|
|
pPartInfo->PartitionType = PARTITION_FAT_16;
|
|
else
|
|
pPartInfo->PartitionType = PARTITION_HUGE;
|
|
*/
|
|
|
|
pPartInfo->PartitionType = PARTITION_HUGE;
|
|
pPartInfo->RecognizedPartition = TRUE;
|
|
}
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|
|
/*
|
|
Protected method: AddError
|
|
|
|
Purpose: Add a error message to a string .The error message will be formatted like this:
|
|
<Disk number: >< My error message > [ System error message ]
|
|
|
|
Parameters: [IN/OUT] CString& strErrors
|
|
The string that must be appended with the error message
|
|
[IN] UINT unErrorMsg
|
|
Our error message. ID of a string from resources
|
|
[IN] BOOL bAddSystemMsg
|
|
TRUE if the latest error system message must be added to our message
|
|
|
|
Return value: -
|
|
*/
|
|
|
|
void CDiskMap::AddError(
|
|
CString& strErrors,
|
|
UINT unErrorMsg,
|
|
BOOL bAddSystemMsg /* =FALSE */ )
|
|
{
|
|
MY_TRY
|
|
|
|
CString str, strName, strErr, strSystemErr;
|
|
|
|
// Get system error message
|
|
if( bAddSystemMsg )
|
|
{
|
|
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.
|
|
{
|
|
strSystemErr = (LPCTSTR)lpMsgBuf;
|
|
LocalFree( lpMsgBuf );
|
|
}
|
|
}
|
|
|
|
strName.Format( IDS_DISK, m_dwDiskNumber );
|
|
|
|
// Get my error message
|
|
strErr.LoadString(unErrorMsg);
|
|
|
|
str.Format(_T("%s: %s %s\n"), strName, strErr, strSystemErr );
|
|
strErrors += str;
|
|
|
|
MY_CATCH_AND_THROW
|
|
}
|
|
|