windows-nt/Source/XPSP1/NT/drivers/ftapi/ftman/diskmap.cpp
2020-09-26 16:20:57 +08:00

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
}