Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1705 lines
52 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. FTMan
  5. File Name:
  6. DiskMap.cpp
  7. Abstract:
  8. Implementation of classes used to keep the disk array map in memory. Used in retrieving partitions and free spaces,
  9. in creating and deleting partitions
  10. Author:
  11. Cristian Teodorescu October 23, 1998
  12. Notes:
  13. Revision History:
  14. --*/
  15. #include "stdafx.h"
  16. #include "DiskMap.h"
  17. #include "FrSpace.h"
  18. #include "PhPart.h"
  19. #include "Resource.h"
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. //#define CSEC_FAT 32680
  26. //#define CSEC_FAT32MEG 65536
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // CDiskMap
  29. ////////////////////////////////////////////////////////////////////////////////////////////////////
  30. // Public methods
  31. /*
  32. Public method: LoadDiskInfo
  33. Purpose: Load the disk layout and geometry into this object
  34. Parameters: [OUT] CString& strErrors
  35. All errors found during LoadDiskInfo are reported through this string
  36. [OUT] BOOL& bMissingDisk
  37. Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
  38. This may notify the calling procedure that the search for installed disks is over
  39. Return value: TRUE if the disk information is loaded successfully
  40. */
  41. BOOL CDiskMap::LoadDiskInfo(
  42. CString& strErrors,
  43. BOOL& bMissingDisk )
  44. {
  45. MY_TRY
  46. ASSERT( m_dwDiskNumber >= 0 );
  47. m_bLoaded = FALSE;
  48. strErrors = _T("");
  49. bMissingDisk = FALSE;
  50. CString strFileName;
  51. strFileName.Format(_T("\\\\.\\PHYSICALDRIVE%lu"), m_dwDiskNumber);
  52. // Try to open the disk
  53. HANDLE hDisk = CreateFile(
  54. strFileName, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  55. NULL, OPEN_EXISTING, 0 , INVALID_HANDLE_VALUE );
  56. if( hDisk == INVALID_HANDLE_VALUE )
  57. {
  58. // This disk is not installed in the system
  59. bMissingDisk = TRUE;
  60. return FALSE;
  61. }
  62. // Read the geometry of the disk
  63. ULONG ulBytes;
  64. DISK_GEOMETRY geometry;
  65. if( !DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
  66. &geometry, sizeof(geometry), &ulBytes, NULL ) )
  67. {
  68. AddError( strErrors, IDS_ERR_GET_DRIVE_GEOMETRY, TRUE );
  69. CloseHandle( hDisk );
  70. return FALSE;
  71. }
  72. m_llSectorSize = geometry.BytesPerSector;
  73. m_llTrackSize = geometry.SectorsPerTrack * m_llSectorSize;
  74. m_llCylinderSize = geometry.TracksPerCylinder * m_llTrackSize;
  75. m_llDiskSize = geometry.Cylinders.QuadPart * m_llCylinderSize;
  76. // Allocate some space for the drive layout buffer
  77. if( m_pBuffer == NULL )
  78. {
  79. ASSERT( m_dwBufferSize == 0 );
  80. m_dwBufferSize = sizeof( DRIVE_LAYOUT_INFORMATION ) + 20*sizeof(PARTITION_INFORMATION);
  81. // Allocate space for the drive layout information
  82. m_pBuffer = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc(0, m_dwBufferSize);
  83. if( !m_pBuffer )
  84. {
  85. m_dwBufferSize = 0;
  86. CloseHandle( hDisk );
  87. AddError( strErrors, IDS_ERR_ALLOCATION, FALSE );
  88. // There is nothing else to do so return
  89. return FALSE;
  90. }
  91. }
  92. // Read the structure of the disk
  93. BOOL bResult = DeviceIoControl(
  94. hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0,
  95. m_pBuffer, m_dwBufferSize, &ulBytes, NULL );
  96. // If the buffer is not large enough reallocate it
  97. while (!bResult && GetLastError() == ERROR_MORE_DATA)
  98. {
  99. m_dwBufferSize = ulBytes;
  100. LocalFree( m_pBuffer );
  101. m_pBuffer = (PDRIVE_LAYOUT_INFORMATION)(LocalAlloc(0, m_dwBufferSize ));
  102. if (!m_pBuffer)
  103. {
  104. m_dwBufferSize = 0;
  105. CloseHandle( hDisk );
  106. AddError( strErrors, IDS_ERR_ALLOCATION, FALSE );
  107. return FALSE;
  108. }
  109. BOOL bResult = DeviceIoControl(
  110. hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0,
  111. m_pBuffer, m_dwBufferSize, &ulBytes, NULL );
  112. }
  113. // If we still cannot get the drive layout then return false
  114. if( !bResult )
  115. {
  116. AddError( strErrors, IDS_ERR_GET_DRIVE_LAYOUT, TRUE );
  117. CloseHandle(hDisk);
  118. return FALSE;
  119. }
  120. m_bLoaded = TRUE;
  121. return TRUE;
  122. MY_CATCH_AND_THROW
  123. }
  124. ////////////////////////////////////////////////////////////////////////////////////////////////////
  125. // Public methods
  126. /*
  127. Public method: SaveDiskInfo
  128. Purpose: Save the disk layout on disk
  129. Parameters: [OUT] CString& strErrors
  130. All errors found during SaveDiskInfo are reported through this string
  131. [OUT] BOOL& bMissingDisk
  132. Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
  133. This may notify the calling procedure that the search for installed disks is over
  134. Return value: TRUE if the disk information is saved successfully
  135. */
  136. BOOL CDiskMap::SaveDiskInfo(
  137. CString& strErrors,
  138. BOOL& bMissingDisk )
  139. {
  140. MY_TRY
  141. ASSERT( m_dwDiskNumber >= 0 );
  142. strErrors = _T("");
  143. bMissingDisk = FALSE;
  144. CString strFileName;
  145. strFileName.Format(_T("\\\\.\\PHYSICALDRIVE%lu"), m_dwDiskNumber);
  146. // Try to open the disk
  147. HANDLE hDisk = CreateFile(
  148. strFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  149. NULL, OPEN_EXISTING, 0 , INVALID_HANDLE_VALUE );
  150. if( hDisk == INVALID_HANDLE_VALUE )
  151. {
  152. // This disk is not installed in the system
  153. bMissingDisk = TRUE;
  154. return FALSE;
  155. }
  156. ULONG ulBytes;
  157. BOOL bResult = DeviceIoControl(
  158. hDisk, IOCTL_DISK_SET_DRIVE_LAYOUT, m_pBuffer,
  159. sizeof(DRIVE_LAYOUT_INFORMATION) + m_pBuffer->PartitionCount*sizeof(PARTITION_INFORMATION),
  160. NULL, 0, &ulBytes, NULL );
  161. if( !bResult )
  162. {
  163. AddError( strErrors, IDS_ERR_SET_DRIVE_LAYOUT, TRUE );
  164. CloseHandle( hDisk );
  165. return FALSE;
  166. }
  167. CloseHandle( hDisk );
  168. m_bLoaded = TRUE;
  169. return TRUE;
  170. MY_CATCH_AND_THROW
  171. }
  172. /*
  173. Public method: ReadPartitions
  174. Purpose: Retrieve all non-container partitions on the disk and create CPhysicalPartitionData instances for
  175. them
  176. If the disk info is not loaded the method calls LoadDiskInfo first
  177. Parameters: [OUT] CObArray& arrPartitions
  178. Array of CPhysicalPartitionData containing all physical partitions found on the disk
  179. [OUT] CString& strErrors
  180. All errors found during ReadPhysicalPartitions are reported through this string
  181. [OUT] BOOL& bMissingDisk
  182. Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
  183. This may notify the calling procedure that the search for installed disks is over
  184. [IN] CItemData* pParentData
  185. The items' parent data. It will fill the m_pParentData member of all new
  186. CPhysicalPartititionData instances
  187. Return value: TRUE if the the method ends successfully
  188. */
  189. BOOL CDiskMap::ReadPartitions(
  190. CObArray& arrPartitions,
  191. CString& strErrors,
  192. BOOL& bMissingDisk,
  193. CItemData* pParentData /* = NULL */)
  194. {
  195. MY_TRY
  196. ASSERT( m_dwDiskNumber >= 0 );
  197. arrPartitions.RemoveAll();
  198. strErrors = _T("");
  199. if( m_bLoaded )
  200. bMissingDisk = FALSE;
  201. else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  202. return FALSE;
  203. // Read all recognized partitions on the disk
  204. for( DWORD dwIndex = 0; dwIndex < m_pBuffer->PartitionCount; dwIndex++ )
  205. {
  206. // A partition that is not logical volume has the highest bit of field
  207. // PartitionType 0
  208. if( m_pBuffer->PartitionEntry[dwIndex].RecognizedPartition &&
  209. !( m_pBuffer->PartitionEntry[dwIndex].PartitionType & 0x80 ) )
  210. {
  211. // Create the logical volume item data
  212. CPhysicalPartitionData* pData = new CPhysicalPartitionData(
  213. m_dwDiskNumber,
  214. m_pBuffer->Signature,
  215. &(m_pBuffer->PartitionEntry[dwIndex]),
  216. ( dwIndex < 4 ) ? PT_Primary : PT_InExtendedPartition,
  217. pParentData,
  218. TRUE);
  219. CString strMemberErrors;
  220. pData->ReadItemInfo( strMemberErrors );
  221. strErrors += strMemberErrors;
  222. // Insert the structure in the members' data array This array must be sorted by starting offset
  223. for( int i = 0; i < arrPartitions.GetSize(); i++ )
  224. {
  225. if( pData->m_PartInfo.StartingOffset.QuadPart < ((CPhysicalPartitionData*)arrPartitions[i])->m_PartInfo.StartingOffset.QuadPart )
  226. break;
  227. }
  228. arrPartitions.InsertAt( i, pData );
  229. }
  230. }
  231. return TRUE;
  232. MY_CATCH_AND_THROW
  233. }
  234. /*
  235. Public method: ReadFreeSpaces
  236. Purpose: Retrieve all free spaces on the disk and create CFreeSpaceData instances for
  237. them. A free space is a space not contained by a physical partition
  238. If the disk info is not loaded the method calls LoadDiskInfo first
  239. Parameters: [OUT] CObArray& arrFreeSpaces
  240. Array of CFreeSpaceData containing all free spaces found on the disk
  241. [OUT] CString& strErrors
  242. All errors found during ReadFreeSpaces are reported through this string
  243. [OUT] BOOL& bMissingDisk
  244. Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
  245. This may notify the calling procedure that the search for installed disks is over
  246. [IN] CItemData* pParentData
  247. The items' parent data. It will fill the m_pParentData member of all new
  248. CFreeSpaceData instances
  249. Return value: TRUE if the method ends successfully
  250. */
  251. BOOL CDiskMap::ReadFreeSpaces(
  252. CObArray& arrFreeSpaces,
  253. CString& strErrors,
  254. BOOL& bMissingDisk,
  255. CItemData* pParentData /* = NULL */)
  256. {
  257. MY_TRY
  258. ASSERT( m_dwDiskNumber >= 0 );
  259. arrFreeSpaces.RemoveAll();
  260. strErrors = _T("");
  261. if( m_bLoaded )
  262. bMissingDisk = FALSE;
  263. else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  264. return FALSE;
  265. DWORD dwNextIndex;
  266. GetExtendedPartitionFreeSpaces( 0, m_llDiskSize, 0, dwNextIndex, arrFreeSpaces, pParentData );
  267. for( int i = 0; i < arrFreeSpaces.GetSize(); i++ )
  268. ((CFreeSpaceData*)(arrFreeSpaces[i]))->m_dwFreeSpaceNumber = i+1;
  269. return TRUE;
  270. MY_CATCH_AND_THROW
  271. }
  272. /*
  273. Public method: ReadPartitionInformation
  274. Purpose: Retrieves a partition information from the disk (m_dwDiskNumber) layout
  275. Parameters: [IN] LONGLONG llPartStartOffset
  276. Offset of the partition
  277. [OUT] PARTITION_INFORMATION& partInfo
  278. Structure to receive the partition info
  279. [OUT] PARTITION_TYPE& wPartitionType
  280. Partition type ( primary or partition in ectended partition )
  281. [OUT] CString& strErrors
  282. All errors found during ReadPartitionInformation are reported through this string
  283. [OUT] BOOL& bMissingDisk
  284. Reported TRUE if the disk m_dwDiskNumber is not found ( CreateFile returns invalid handle )
  285. This may notify the calling procedure that the search for installed disks is over
  286. Return value: TRUE if the query succeeded
  287. */
  288. BOOL CDiskMap::ReadPartitionInformation(
  289. LONGLONG llPartStartOffset,
  290. PARTITION_INFORMATION& partInfo,
  291. PARTITION_TYPE& wPartitionType,
  292. CString& strErrors,
  293. BOOL& bMissingDisk )
  294. {
  295. MY_TRY
  296. ASSERT( m_dwDiskNumber >= 0 );
  297. strErrors = _T("");
  298. if( m_bLoaded )
  299. bMissingDisk = FALSE;
  300. else if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  301. return FALSE;
  302. // Check all recognized partitions on the disk
  303. for( DWORD dwIndex = 0; dwIndex < m_pBuffer->PartitionCount; dwIndex++ )
  304. {
  305. if( ( m_pBuffer->PartitionEntry[dwIndex].RecognizedPartition ) &&
  306. ( m_pBuffer->PartitionEntry[dwIndex].StartingOffset.QuadPart == llPartStartOffset ) )
  307. {
  308. wPartitionType = ( dwIndex < 4 ) ? PT_Primary : PT_InExtendedPartition;
  309. memcpy(&partInfo, &(m_pBuffer->PartitionEntry[dwIndex]), sizeof(PARTITION_INFORMATION) );
  310. return TRUE;
  311. }
  312. }
  313. // The partition was not found
  314. AddError( strErrors, IDS_ERR_PARTITION_NOT_FOUND, FALSE );
  315. return FALSE;
  316. MY_CATCH_AND_THROW
  317. }
  318. /*
  319. Public method: DeletePartition
  320. Purpose: Deletes a partition of the disk ( m_dwDiskNumber )
  321. Parameters: [IN] LONGLONG llPartStartOffset
  322. Offset of the partition
  323. Return value: TRUE if the deletion succeeded
  324. */
  325. BOOL CDiskMap::DeletePartition(
  326. LONGLONG llPartStartOffset )
  327. {
  328. MY_TRY
  329. ASSERT( m_dwDiskNumber >= 0 );
  330. CString strErrors;
  331. BOOL bMissingDisk;
  332. if( !m_bLoaded )
  333. {
  334. if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  335. {
  336. if( bMissingDisk )
  337. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  338. AfxMessageBox( strErrors, MB_ICONSTOP );
  339. return FALSE;
  340. }
  341. }
  342. // Retrieve the indexes in the partition table of the partition itself and of its parent extended partition ( if any )
  343. DWORD dwPartIndex, dwParentIndex, dwNextIndex;
  344. if( !SearchForPartitionInExtendedPartition( llPartStartOffset, MAXDWORD, 0, dwNextIndex, dwPartIndex, dwParentIndex ) )
  345. {
  346. // The partition was not found
  347. AfxMessageBox( IDS_ERR_PARTITION_NOT_FOUND, MB_ICONSTOP );
  348. return FALSE;
  349. }
  350. // Delete the partition from its parent table
  351. DeletePartitionFromTable( dwPartIndex );
  352. // Update / Delete the parent extended partition ( depends on what's left inside it )
  353. if( dwParentIndex != MAXDWORD ) // The partition was a member of an extended partition
  354. UpdateExtendedPartitionAfterMemberDeletion( dwParentIndex, ((DWORD)( dwPartIndex / 4 ))*4 );
  355. // Time to save on the disk
  356. if( !SaveDiskInfo( strErrors, bMissingDisk ) )
  357. {
  358. if( bMissingDisk )
  359. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  360. AfxMessageBox( strErrors, MB_ICONSTOP );
  361. return FALSE;
  362. }
  363. return TRUE;
  364. MY_CATCH_AND_THROW
  365. }
  366. /*
  367. Public method: DeleteExtendedPartition
  368. Purpose: Delete an extended partition of the disk ( m_dwDiskNumber ).
  369. The extended partition should not be contained by another extended partition
  370. Parameters: [IN] LONGLONG llPartStartOffset
  371. Offset of the extended partition
  372. Return value: TRUE if the deletion succeeded
  373. */
  374. BOOL CDiskMap::DeleteExtendedPartition(
  375. LONGLONG llPartStartOffset )
  376. {
  377. MY_TRY
  378. ASSERT( m_dwDiskNumber >= 0 );
  379. CString strErrors;
  380. BOOL bMissingDisk;
  381. if( !m_bLoaded )
  382. {
  383. if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  384. {
  385. if( bMissingDisk )
  386. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  387. AfxMessageBox( strErrors, MB_ICONSTOP );
  388. return FALSE;
  389. }
  390. }
  391. // The real offset of the extended partition is one track before the offset of the free space
  392. llPartStartOffset -= m_llTrackSize;
  393. // Search the extended partition in the disk 4 slots table
  394. DWORD dwMemberCount = ( 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount;
  395. for( UINT i = 0; i < dwMemberCount; i++ )
  396. {
  397. PARTITION_INFORMATION* pPart = &(m_pBuffer->PartitionEntry[i]);
  398. if( IsContainerPartition( pPart->PartitionType ) && ( pPart->StartingOffset.QuadPart == llPartStartOffset ) )
  399. break;
  400. }
  401. if( i == dwMemberCount )
  402. {
  403. // Partition not found
  404. AfxMessageBox( IDS_ERR_PARTITION_NOT_FOUND, MB_ICONSTOP );
  405. return FALSE;
  406. }
  407. DeletePartitionFromTable(i);
  408. // Time to save on the disk
  409. if( !SaveDiskInfo( strErrors, bMissingDisk ) )
  410. {
  411. if( bMissingDisk )
  412. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  413. AfxMessageBox( strErrors, MB_ICONSTOP );
  414. return FALSE;
  415. }
  416. return TRUE;
  417. MY_CATCH_AND_THROW
  418. }
  419. /*
  420. Public method: CreatePartition
  421. Purpose: Create a partition on the disk ( m_dwDiskNumber )
  422. Parameters: [IN] LONGLONG llPartStartOffset
  423. Aproximative starting offset of the partition ( this might be modified due to
  424. the creation of an extra container for this partition
  425. [IN] LONGLONG llPartSize
  426. Aproximative size of the partition ( this should be rounded-up to the next cylinder border )
  427. [OUT] LONGLONG& llExactPartStartOffset
  428. The exact starting offset of the partition
  429. Return value: TRUE if the creation succeeded
  430. */
  431. BOOL CDiskMap::CreatePartition(
  432. LONGLONG llPartStartOffset,
  433. LONGLONG llPartSize,
  434. LONGLONG& llExactPartStartOffset)
  435. {
  436. MY_TRY
  437. ASSERT( m_dwDiskNumber >= 0 );
  438. CString strErrors;
  439. BOOL bMissingDisk;
  440. if( !m_bLoaded )
  441. {
  442. if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  443. {
  444. if( bMissingDisk )
  445. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  446. AfxMessageBox( strErrors, MB_ICONSTOP );
  447. return FALSE;
  448. }
  449. }
  450. LONGLONG llPartEndOffset = GetCylinderBorderAfter( llPartStartOffset + llPartSize );
  451. LONGLONG llPartTrueSize = llPartEndOffset - llPartStartOffset;
  452. LONGLONG llFreeSpaceStartOffset;
  453. LONGLONG llFreeSpaceEndOffset;
  454. BOOL bAtBeginningOfExtendedPartition;
  455. DWORD dwNewPartIndex;
  456. if( !SearchForFreeSpaceInExtendedPartition( llPartStartOffset, llPartTrueSize, 0, m_llDiskSize, 0,
  457. llFreeSpaceStartOffset, llFreeSpaceEndOffset,
  458. bAtBeginningOfExtendedPartition, dwNewPartIndex) )
  459. {
  460. // The free space was not found
  461. AfxMessageBox( IDS_ERR_FREE_SPACE_NOT_FOUND, MB_ICONSTOP );
  462. return FALSE;
  463. }
  464. // If the partition must be created inside a container and it can't be created at the beginning of
  465. // this container then a new container must be created for it
  466. BOOL bCreateContainer = ( ( dwNewPartIndex >= 4 ) && !bAtBeginningOfExtendedPartition );
  467. if( !AddPartitionToTable( llPartStartOffset, llPartTrueSize, dwNewPartIndex, bCreateContainer, TRUE ) )
  468. return FALSE;
  469. if( bCreateContainer )
  470. llExactPartStartOffset = llPartStartOffset + m_llTrackSize;
  471. else
  472. llExactPartStartOffset = llPartStartOffset;
  473. // Time to save on the disk
  474. if( !SaveDiskInfo( strErrors, bMissingDisk ) )
  475. {
  476. if( bMissingDisk )
  477. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  478. AfxMessageBox( strErrors, MB_ICONSTOP );
  479. return FALSE;
  480. }
  481. return TRUE;
  482. MY_CATCH_AND_THROW
  483. }
  484. /*
  485. Public method: CreateExtendedPartition
  486. Purpose: Create an extended partition on the disk ( m_dwDiskNumber )
  487. Parameters: [IN] LONGLONG llPartStartOffset
  488. Offset of the partition
  489. [IN] LONGLONG llPartSize
  490. Estimate size of the partition ( this should be rounded-up to the next cylinder border )
  491. [OUT] LONGLONG& llNewFreeSpaceOffset
  492. The starting offset of the newly created free space inside the new empty extended partition
  493. Usually it is the starting offset of the extended partition + a track size
  494. Return value: TRUE if the creation succeeded
  495. */
  496. BOOL CDiskMap::CreateExtendedPartition(
  497. LONGLONG llPartStartOffset,
  498. LONGLONG llPartSize,
  499. LONGLONG& llNewFreeSpaceOffset )
  500. {
  501. MY_TRY
  502. ASSERT( m_dwDiskNumber >= 0 );
  503. CString strErrors;
  504. BOOL bMissingDisk;
  505. if( !m_bLoaded )
  506. {
  507. if( !LoadDiskInfo( strErrors, bMissingDisk ) )
  508. {
  509. if( bMissingDisk )
  510. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  511. AfxMessageBox( strErrors, MB_ICONSTOP );
  512. return FALSE;
  513. }
  514. }
  515. LONGLONG llPartEndOffset = GetCylinderBorderAfter( llPartStartOffset + llPartSize );
  516. LONGLONG llPartTrueSize = llPartEndOffset - llPartStartOffset;
  517. LONGLONG llFreeSpaceStartOffset;
  518. LONGLONG llFreeSpaceEndOffset;
  519. BOOL bAtBeginningOfExtendedPartition;
  520. DWORD dwNewPartIndex;
  521. if( !SearchForFreeSpaceInExtendedPartition( llPartStartOffset, llPartTrueSize, 0, m_llDiskSize, 0,
  522. llFreeSpaceStartOffset, llFreeSpaceEndOffset,
  523. bAtBeginningOfExtendedPartition, dwNewPartIndex) ||
  524. ( dwNewPartIndex >= 4 ) )
  525. {
  526. // The free space was not found or was found in another extended partition
  527. AfxMessageBox( IDS_ERR_FREE_SPACE_NOT_FOUND, MB_ICONSTOP );
  528. return FALSE;
  529. }
  530. if( !AddPartitionToTable( llPartStartOffset, llPartTrueSize, dwNewPartIndex, TRUE, FALSE ) )
  531. return FALSE;
  532. // Time to save on the disk
  533. if( !SaveDiskInfo( strErrors, bMissingDisk ) )
  534. {
  535. if( bMissingDisk )
  536. strErrors.Format( IDS_ERR_MISSING_DISK, m_dwDiskNumber );
  537. AfxMessageBox( strErrors, MB_ICONSTOP );
  538. return FALSE;
  539. }
  540. llNewFreeSpaceOffset = llPartStartOffset + m_llTrackSize;
  541. return TRUE;
  542. MY_CATCH_AND_THROW
  543. }
  544. ////////////////////////////////////////////////////////////////////////////////////////////////////
  545. // Protected methods
  546. /*
  547. Protected method: GetNextIndex
  548. Purpose: Given an extended partition table index in m_pBuffer->PartitionInfo, scan recursively
  549. the extended partition tree and find the chunk of m_pBuffer->PartitionInfo filled with
  550. members of the extended partition
  551. Then return the next index. This index is the index of the following extended partition
  552. from the same ( or superior ) level as the given extended partition
  553. Parameters: [IN] DWORD dwTableIndex
  554. Index in m_pBuffer->PartitionInfo of the extended partition
  555. ( 0 for the whole disk );
  556. Return value: Index in m_pBuffer->PartitionEntry of the next extended partition table on the same level
  557. */
  558. DWORD CDiskMap::GetNextIndex( DWORD dwTableIndex )
  559. {
  560. MY_TRY
  561. ASSERT( m_bLoaded );
  562. ASSERT( dwTableIndex >= 0 );
  563. ASSERT( dwTableIndex%4 == 0 );
  564. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  565. DWORD dwNextIndex = dwTableIndex + 4;
  566. // 1. Scan the partition table. Compute the end offset for every member. For container members retrieve all
  567. // their free spaces.
  568. for( UINT i = 0; ( i < 4 ) && ( dwTableIndex + i < m_pBuffer->PartitionCount ); i++ )
  569. {
  570. if( IsContainerPartition( pTable[i].PartitionType ) )
  571. dwNextIndex = GetNextIndex( dwNextIndex );
  572. }
  573. return dwNextIndex;
  574. MY_CATCH_AND_THROW
  575. }
  576. /*
  577. Protected method: GetExtendedPartitionFreeSpaces
  578. Purpose: Search recursively for free spaces inside an extended partition given its starting offset,
  579. size and partitions table index inside m_pBuffer
  580. This method may be called also to get the free spaces of the whole disk.
  581. For every found free space create a CFreeSpace instance and add it to arrFreeSpaces
  582. Parameters: [IN] LONGLONG llExtPartStartOffset
  583. The start offset of the extended partition ( 0 for the whole disk )
  584. [IN] LONGLONG llExtPartEndOffset
  585. The end offset of the extended partition ( m_llDiskSize for the whole disk )
  586. [IN] DWORD dwTableIndex
  587. Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
  588. ( 0 for the whole disk )
  589. [OUT] DWORD& dwNextIndex
  590. Index in m_pBuffer->PartitionEntry of the next extended partition table
  591. [OUT] CObArray& arrFreeSpaces
  592. Array of CFreeSpaceData containing all free spaces found in this extended partition (disk)
  593. [IN] CItemData* pParentData
  594. The items' parent data. It will fill the m_pParentData member of all new
  595. CFreeSpaceData instances
  596. Return value: -
  597. */
  598. void CDiskMap::GetExtendedPartitionFreeSpaces(
  599. LONGLONG llExtPartStartOffset,
  600. LONGLONG llExtPartEndOffset,
  601. DWORD dwTableIndex,
  602. DWORD& dwNextIndex,
  603. CObArray& arrFreeSpaces,
  604. CItemData* pParentData /* = NULL */)
  605. {
  606. MY_TRY
  607. ASSERT( m_bLoaded );
  608. ASSERT( dwTableIndex >= 0 );
  609. ASSERT( dwTableIndex%4 == 0 );
  610. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  611. CObArray arrPartFreeSpaces[4];
  612. LONGLONG arrPartEndOffset[4];
  613. PARTITION_INFORMATION* pPart;
  614. DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
  615. // 1. Order the members by starting offset ( they may be not ordered in pTable )
  616. DWORD arrOrder[4];
  617. DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
  618. ASSERT( dwPartitionCount <= dwMemberCount );
  619. // 2. Scan the partition table. For container members retrieve all their free spaces.
  620. dwNextIndex = dwTableIndex + 4;
  621. DWORD dwExtendedPartitionCountOnLevel = 0;
  622. for( UINT i = 0; i < dwMemberCount; i++ )
  623. {
  624. pPart = &(pTable[i]);
  625. if( pPart->PartitionType == 0 )
  626. continue;
  627. if( IsContainerPartition( pPart->PartitionType ) )
  628. {
  629. if( dwTableIndex == 0 )
  630. {
  631. // This is a root container partition. Its superior limit is given by its size
  632. arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
  633. }
  634. else
  635. {
  636. // This is a container inside another container. Its superior limit is given by the starting offset
  637. // of the next partition ( as position ) or by the end of its parent container
  638. for( UINT j = 0; j < dwPartitionCount; j++ )
  639. {
  640. if( arrOrder[j] == i )
  641. break;
  642. }
  643. ASSERT( j < dwPartitionCount );
  644. if( j < dwPartitionCount - 1 )
  645. arrPartEndOffset[i] = pTable[ arrOrder[j+1] ].StartingOffset.QuadPart;
  646. else
  647. arrPartEndOffset[i] = llExtPartEndOffset;
  648. }
  649. GetExtendedPartitionFreeSpaces(
  650. pPart->StartingOffset.QuadPart, arrPartEndOffset[i],
  651. dwNextIndex, dwNextIndex, arrPartFreeSpaces[i], pParentData );
  652. dwExtendedPartitionCountOnLevel++;
  653. }
  654. else
  655. arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
  656. }
  657. // 3. Now retrieve all free spaces inside this extended partition and add them to arrFreeSpaces
  658. // together with the free spaces of the container members. arrFreeSpaces must be sorted by starting offset
  659. // The first track of the disk / extended partition is reserved
  660. LONGLONG llFreeSpaceStartOffset = llExtPartStartOffset + m_llTrackSize;
  661. LONGLONG llFreeSpaceEndOffset;
  662. for( i = 0; i <= dwPartitionCount ; i++ )
  663. {
  664. // The end of a free space could be the starting offset of the next member or the end of the extended
  665. // partition
  666. if( i == dwPartitionCount )
  667. llFreeSpaceEndOffset = llExtPartEndOffset;
  668. else
  669. {
  670. pPart = &(pTable[ arrOrder[i] ]);
  671. // Always take the greatest cylinder border less than or equal with the starting offset of the next member
  672. llFreeSpaceEndOffset = GetCylinderBorderBefore(pPart->StartingOffset.QuadPart);
  673. }
  674. // A free space must be at least 1 cylinder size long
  675. if( llFreeSpaceStartOffset + m_llCylinderSize <= llFreeSpaceEndOffset )
  676. {
  677. FREE_SPACE_TYPE wFreeSpaceType;
  678. if( dwTableIndex == 0 ) // This is a free space between primary partitions
  679. wFreeSpaceType = FST_Primary;
  680. else if( dwPartitionCount > 0 ) // This is a free space inside a non-empty extended partition
  681. wFreeSpaceType = FST_InExtendedPartition;
  682. else // This is an empty extended partition
  683. wFreeSpaceType = FST_EmptyExtendedPartition;
  684. // We found a free space !!!
  685. CFreeSpaceData* pData = new CFreeSpaceData(
  686. m_dwDiskNumber,
  687. m_pBuffer->Signature,
  688. llFreeSpaceStartOffset,
  689. llFreeSpaceEndOffset - llFreeSpaceStartOffset,
  690. wFreeSpaceType,
  691. m_llCylinderSize,
  692. dwPartitionCount - dwExtendedPartitionCountOnLevel,
  693. dwExtendedPartitionCountOnLevel,
  694. pParentData
  695. );
  696. CString strMemberErrors;
  697. pData->ReadItemInfo( strMemberErrors );
  698. ASSERT( strMemberErrors.IsEmpty() );
  699. // Add the structure to the members' data array
  700. arrFreeSpaces.Add(pData);
  701. }
  702. if( i != dwPartitionCount )
  703. {
  704. // The starting offset of the next free space could be the end offset of the current member
  705. // Always take the lower cylinder border greater or equal with this value
  706. llFreeSpaceStartOffset = GetCylinderBorderAfter( arrPartEndOffset[ arrOrder[i] ] );
  707. if( IsContainerPartition( pPart->PartitionType ) )
  708. arrFreeSpaces.Append( arrPartFreeSpaces[ arrOrder[i] ] );
  709. }
  710. }
  711. return;
  712. MY_CATCH_AND_THROW
  713. }
  714. /*
  715. Protected method: SortPartitionsByOffset
  716. Purpose: Sort a partition table by starting offset without actually changing the table
  717. Parameters: [IN] PARTITION_INFORMATION* arrTable
  718. Array of partitions ( inside m_pBuffer )
  719. [IN] DWORD
  720. The size of the array
  721. [OUT] DWORD* arrOrder
  722. Array of indexes in arrTable of partitions sorted by starting offset
  723. The null partitions are not considered at all
  724. Return value: The number of not null sorted partitions
  725. */
  726. DWORD CDiskMap::SortPartitionsByOffset(
  727. PARTITION_INFORMATION* arrTable,
  728. DWORD dwSize,
  729. DWORD* arrOrder )
  730. {
  731. MY_TRY
  732. DWORD dwOrderedSize = 0;
  733. for( DWORD i = 0; i < dwSize; i++ )
  734. {
  735. // Just ignore null partitions
  736. if( arrTable[i].PartitionType == 0 )
  737. continue;
  738. for( DWORD j = 0; j < dwOrderedSize; j++ )
  739. {
  740. if( arrTable[i].StartingOffset.QuadPart < arrTable[arrOrder[j]].StartingOffset.QuadPart )
  741. break;
  742. }
  743. for( DWORD k = dwOrderedSize; k > j; k-- )
  744. arrOrder[k] = arrOrder[k-1];
  745. arrOrder[j] = i;
  746. dwOrderedSize++;
  747. }
  748. return dwOrderedSize;
  749. MY_CATCH_AND_THROW
  750. }
  751. /*
  752. Protected method: GetCylinderBorderBefore
  753. Purpose: Get the greatest cylinder border less than or equal with an offset
  754. Parameters: [IN] LONGLONG llOffset
  755. The offset
  756. Return value: The greatest cylinder border less than or equal with llOffset
  757. */
  758. LONGLONG CDiskMap::GetCylinderBorderBefore( LONGLONG llOffset )
  759. {
  760. // This is the most common case so treat it separately
  761. if( llOffset%m_llCylinderSize == 0 )
  762. return llOffset;
  763. //And this the generic formula
  764. return ((LONGLONG)(llOffset / m_llCylinderSize)) * m_llCylinderSize;
  765. }
  766. /*
  767. Protected method: GetCylinderBorderAfter
  768. Purpose: Get the lowest cylinder border greater than or equal with an offset
  769. Parameters: [IN] LONGLONG llOffset
  770. The offset
  771. Return value: The lowest cylinder border greater than or equal with llOffset
  772. */
  773. LONGLONG CDiskMap::GetCylinderBorderAfter( LONGLONG llOffset )
  774. {
  775. // This is the most common case so treat it separately
  776. if( llOffset%m_llCylinderSize == 0 )
  777. return llOffset;
  778. // And this is the generic formula
  779. return ((LONGLONG)((llOffset + m_llCylinderSize - 1) / m_llCylinderSize)) * m_llCylinderSize;
  780. }
  781. /*
  782. Protected method: SearchForPartitionInExtendedPartition
  783. Purpose: Search recursively for a partition inside an extended partition
  784. This method may be called also to search for a partition inside the whole disk.
  785. Parameters: [IN] LONGLONG llPartOffset
  786. The start offset of the partition we are looking for
  787. [IN] DWORD dwExtPartIndex
  788. Index in m_pBuffer->PartitionEntry of the extended partition
  789. ( MAXDWORD for the whole disk because we don't have -1 available )
  790. [IN] DWORD dwTableIndex
  791. Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
  792. ( 0 for the whole disk )
  793. [OUT] DWORD& dwNextIndex
  794. Index in m_pBuffer->PartitionEntry of the next extended partition table
  795. [OUT] DWORD& dwPartIndex
  796. Index in m_pBuffer->PartitionEntry of the partition we are looking for ( if found )
  797. [OUT] DWORD& dwParentIndex
  798. Index in m_pBuffer->PartitionEntry of the parent partition ( if partition found )
  799. Return value: TRUE if the partition was found inside this extended partition ( disk )
  800. */
  801. BOOL CDiskMap::SearchForPartitionInExtendedPartition(
  802. LONGLONG llPartOffset,
  803. DWORD dwExtPartIndex,
  804. DWORD dwTableIndex,
  805. DWORD& dwNextIndex,
  806. DWORD& dwPartIndex,
  807. DWORD& dwParentIndex )
  808. {
  809. MY_TRY
  810. ASSERT( m_bLoaded );
  811. ASSERT( dwTableIndex >= 0 );
  812. ASSERT( dwTableIndex%4 == 0 );
  813. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  814. dwNextIndex = dwTableIndex + 4;
  815. for( UINT i = 0; ( i < 4 ) && ( dwTableIndex + i < m_pBuffer->PartitionCount ) ; i++ )
  816. {
  817. PARTITION_INFORMATION* pPart = &(pTable[i]);
  818. if( pPart->PartitionType == 0 )
  819. continue;
  820. if( IsContainerPartition( pPart->PartitionType ) )
  821. {
  822. // Search in depth
  823. if( SearchForPartitionInExtendedPartition( llPartOffset, dwTableIndex + i, dwNextIndex,
  824. dwNextIndex, dwPartIndex, dwParentIndex ) )
  825. return TRUE;
  826. }
  827. else
  828. {
  829. if( pPart->StartingOffset.QuadPart == llPartOffset )
  830. {
  831. // This is my partition
  832. dwPartIndex = dwTableIndex + i;
  833. dwParentIndex = dwExtPartIndex;
  834. return TRUE;
  835. }
  836. }
  837. }
  838. return FALSE;
  839. MY_CATCH_AND_THROW
  840. }
  841. /*
  842. Protected method: DeletePartitionFromTable
  843. Purpose: Delete a partition from m_pBuffer->PartitionInfo table.
  844. Parameters: [IN] DWORD dwPartIndex
  845. The index of the partition in m_pBuffer->PartitionInfo
  846. Return value: -
  847. */
  848. void CDiskMap::DeletePartitionFromTable( DWORD dwPartIndex )
  849. {
  850. MY_TRY
  851. ASSERT( m_bLoaded );
  852. ASSERT( ( dwPartIndex >= 0 ) && ( dwPartIndex < m_pBuffer->PartitionCount ) );
  853. // Get the 4 slots table of its parent
  854. DWORD dwTableIndex = ((DWORD)( dwPartIndex / 4 )) * 4;
  855. DWORD dwEndIndex = dwTableIndex + 3;
  856. if( dwEndIndex >= m_pBuffer->PartitionCount )
  857. dwEndIndex = m_pBuffer->PartitionCount - 1;
  858. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  859. // Check if the given partition is a container partition
  860. BOOL bContainer = IsContainerPartition( m_pBuffer->PartitionEntry[dwPartIndex].PartitionType );
  861. // If container partition get the index in m_pBuffer->PartitionEntry for the 4 slots table of that partition
  862. DWORD dwPartTableIndex = dwTableIndex + 4;
  863. DWORD dwPartNextIndex;
  864. DWORD dwShift = 0;
  865. if( bContainer )
  866. {
  867. for( DWORD i = dwTableIndex; i < dwPartIndex; i++ )
  868. {
  869. if( IsContainerPartition( m_pBuffer->PartitionEntry[i].PartitionType ) )
  870. dwPartTableIndex = GetNextIndex( dwPartTableIndex );
  871. }
  872. dwPartNextIndex = GetNextIndex( dwPartTableIndex );
  873. dwShift = dwPartNextIndex - dwPartTableIndex;
  874. }
  875. // Move all following partitions one position to the left in the 4 slots table
  876. for( DWORD i = dwPartIndex; i < dwEndIndex; i++ )
  877. {
  878. memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+1]), sizeof( PARTITION_INFORMATION ) );
  879. m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
  880. }
  881. // Fill the last entry in the 4 slots table with zero
  882. memset( &(m_pBuffer->PartitionEntry[ dwEndIndex ]), 0, sizeof( PARTITION_INFORMATION ) );
  883. m_pBuffer->PartitionEntry[dwEndIndex].RewritePartition = TRUE;
  884. // If container partition remove from m_pBuffer->PartitionEntry all entries belonging to this container partition
  885. if( bContainer && ( dwShift > 0 ) )
  886. {
  887. for( DWORD i = dwPartTableIndex; i < m_pBuffer->PartitionCount - dwShift; i++ )
  888. {
  889. memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+dwShift]), sizeof( PARTITION_INFORMATION ) );
  890. m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
  891. }
  892. m_pBuffer->PartitionCount -= dwShift;
  893. }
  894. MY_CATCH_AND_THROW
  895. }
  896. /*
  897. Protected method: UpdateExtendedPartitionAfterMemberDeletion
  898. Purpose: Update / Delete an extended partition after one of its members was deleted
  899. If the extended partition remains empty then it must be deleted
  900. If only one member is left and this member is an extended partition too then replace the
  901. extended partition with its member ( promote the member on the superior level )
  902. All other situations are unlikely to happen so I don't treat them here
  903. Parameters: [IN] DWORD dwPartIndex
  904. The index in m_pBuffer->PartitionInfo of the partition
  905. [IN] DWORD
  906. The index in m_pBuffer->PartitionInfo of the 4 slots table of the partition
  907. Return value: -
  908. */
  909. void CDiskMap::UpdateExtendedPartitionAfterMemberDeletion(
  910. DWORD dwPartIndex,
  911. DWORD dwTableIndex )
  912. {
  913. MY_TRY
  914. ASSERT( m_bLoaded );
  915. ASSERT( ( dwPartIndex >= 0 ) && ( dwPartIndex < m_pBuffer->PartitionCount ) );
  916. ASSERT( dwTableIndex >= 0 );
  917. ASSERT( dwTableIndex%4 == 0 );
  918. // If the extended partition is member of the whole disk then don't touch it
  919. if( dwPartIndex < 4 )
  920. return;
  921. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  922. // Order the members by starting offset
  923. DWORD arrOrder[4];
  924. DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
  925. dwMemberCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
  926. if( dwMemberCount == 0 )
  927. {
  928. // The extended partition is empty so delete it
  929. DeletePartitionFromTable( dwPartIndex );
  930. return;
  931. }
  932. else if( dwMemberCount == 1 )
  933. {
  934. PARTITION_INFORMATION* pPart = &(pTable[ arrOrder[0] ]);
  935. if( IsContainerPartition( pPart->PartitionType ) )
  936. {
  937. // Replace the parent extended partition with this extended partition
  938. memcpy( &(m_pBuffer->PartitionEntry[dwPartIndex]), pPart, sizeof(PARTITION_INFORMATION) );
  939. m_pBuffer->PartitionEntry[dwPartIndex].RewritePartition = TRUE;
  940. // Remove the 4 slots table of the parent extended partition
  941. for( DWORD i = dwTableIndex; ( i + 4 < m_pBuffer->PartitionCount ); i++ )
  942. {
  943. memcpy( &(m_pBuffer->PartitionEntry[i]), &(m_pBuffer->PartitionEntry[i+4]), sizeof( PARTITION_INFORMATION ) );
  944. m_pBuffer->PartitionEntry[i].RewritePartition = TRUE;
  945. }
  946. m_pBuffer->PartitionCount -= 4;
  947. return;
  948. }
  949. }
  950. MY_CATCH_AND_THROW
  951. }
  952. /*
  953. Protected method: SearchForFreeSpaceInExtendedPartition
  954. Purpose: Given an offset and size search recursively for an appropriate free space
  955. inside an extended partition ( or the whole disk ).
  956. Return the start and end offset of the whole free space and the index in the partition table
  957. where a new partition having the given offset and size might be inserted.
  958. Parameters: [IN] LONGLONG llOffset
  959. The start offset of the space we are looking for
  960. [IN] LONGLONG llSize
  961. The size of the space
  962. [IN] LONGLONG llExtPartStartOffset
  963. The start offset of the extended partition ( 0 for the whole disk )
  964. [IN] LONGLONG llExtPartEndOffset
  965. The end offset of the extended partition ( m_llDiskSize for the whole disk )
  966. [IN] DWORD dwTableIndex
  967. Index in m_pBuffer->PartitionEntry of the partition table of the extended partition
  968. ( 0 for the whole disk )
  969. [OUT] LONGLONG& llFreeSpaceStartOffset
  970. The start offset of the appropriate free space
  971. [OUT] LONGLONG& llFreeSpaceEndOffset
  972. The end offset of the appropriate end space
  973. [OUT] BOOL &bAtBeginningOfExtendedPartition
  974. Is the free space at the beginning of the extended partition?
  975. [OUT] DWORD& dwNewPartIndex
  976. The index in m_pBuffer->PartitionEntry where a new partition having the given offset
  977. and size may be inserted
  978. Return value: TRUE if a free space was found
  979. */
  980. BOOL CDiskMap::SearchForFreeSpaceInExtendedPartition(
  981. LONGLONG llOffset,
  982. LONGLONG llSize,
  983. LONGLONG llExtPartStartOffset,
  984. LONGLONG llExtPartEndOffset,
  985. DWORD dwTableIndex,
  986. LONGLONG& llFreeSpaceStartOffset,
  987. LONGLONG& llFreeSpaceEndOffset,
  988. BOOL &bAtBeginningOfExtendedPartition,
  989. DWORD& dwNewPartIndex)
  990. {
  991. MY_TRY
  992. ASSERT( m_bLoaded );
  993. ASSERT( dwTableIndex >= 0 );
  994. ASSERT( dwTableIndex%4 == 0 );
  995. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  996. LONGLONG arrPartEndOffset[4];
  997. PARTITION_INFORMATION* pPart;
  998. DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
  999. // 1. Order the members by starting offset ( they may be not ordered in pTable )
  1000. DWORD arrOrder[4];
  1001. DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
  1002. ASSERT( dwPartitionCount <= dwMemberCount );
  1003. // 2. Scan the partition table. For container members try to create the partition inside them
  1004. DWORD dwNextIndex = dwTableIndex + 4;
  1005. for( UINT i = 0; i < dwMemberCount; i++ )
  1006. {
  1007. pPart = &(pTable[i]);
  1008. if( pPart->PartitionType == 0 )
  1009. continue;
  1010. if( IsContainerPartition( pPart->PartitionType ) )
  1011. {
  1012. if( dwTableIndex == 0 )
  1013. {
  1014. // This is a root container partition. Its superior limit is given by its size
  1015. arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
  1016. }
  1017. else
  1018. {
  1019. // This is a container inside another container. Its superior limit is given by the starting offset
  1020. // of the next partition ( as position ) or by the end of its parent container
  1021. for( UINT j = 0; j < dwPartitionCount; j++ )
  1022. {
  1023. if( arrOrder[j] == i )
  1024. break;
  1025. }
  1026. ASSERT( j < dwPartitionCount );
  1027. if( j < dwPartitionCount - 1 )
  1028. arrPartEndOffset[i] = pTable[ arrOrder[j+1] ].StartingOffset.QuadPart;
  1029. else
  1030. arrPartEndOffset[i] = llExtPartEndOffset;
  1031. }
  1032. if( ( llOffset >= pPart->StartingOffset.QuadPart ) &&
  1033. ( llOffset + llSize <= arrPartEndOffset[i] ) )
  1034. {
  1035. // Create the partition inside this container
  1036. return SearchForFreeSpaceInExtendedPartition( llOffset, llSize,
  1037. pPart->StartingOffset.QuadPart, arrPartEndOffset[i], dwNextIndex,
  1038. llFreeSpaceStartOffset, llFreeSpaceEndOffset, bAtBeginningOfExtendedPartition, dwNewPartIndex );
  1039. }
  1040. else
  1041. dwNextIndex = GetNextIndex(dwNextIndex);
  1042. }
  1043. else
  1044. arrPartEndOffset[i] = pPart->StartingOffset.QuadPart + pPart->PartitionLength.QuadPart;
  1045. }
  1046. // 3. We must search for our free space among the free spaces between the members of this extended partition !!!
  1047. // The first track of the disk / extended partition is reserved
  1048. llFreeSpaceStartOffset = llExtPartStartOffset + m_llTrackSize;
  1049. for( i = 0 ; i <= dwPartitionCount ; i++ )
  1050. {
  1051. // The end of a free space could be the starting offset of the next member or the end of the extended
  1052. // partition
  1053. if( i == dwPartitionCount )
  1054. llFreeSpaceEndOffset = llExtPartEndOffset;
  1055. else
  1056. {
  1057. pPart = &(pTable[ arrOrder[i] ]);
  1058. // Always take the greatest cylinder border less than or equal with the starting offset of the next member
  1059. llFreeSpaceEndOffset = GetCylinderBorderBefore(pPart->StartingOffset.QuadPart);
  1060. }
  1061. // A free space must be at least 1 cylinder size long
  1062. // Check also if the new partition would match in the free space
  1063. if( ( llFreeSpaceStartOffset + m_llCylinderSize <= llFreeSpaceEndOffset ) &&
  1064. ( llOffset >= llFreeSpaceStartOffset) &&
  1065. ( llOffset + llSize <= llFreeSpaceEndOffset) )
  1066. {
  1067. // We found the appropriate free space but there is still a problem
  1068. // What if the 4 slots table is already full?
  1069. if( dwPartitionCount >= dwMemberCount )
  1070. {
  1071. AfxMessageBox( IDS_ERR_PARTITION_TABLE_FULL, MB_ICONSTOP );
  1072. return FALSE;
  1073. }
  1074. bAtBeginningOfExtendedPartition = ( llFreeSpaceStartOffset == llExtPartStartOffset + m_llTrackSize );
  1075. dwNewPartIndex = dwTableIndex + i;
  1076. return TRUE;
  1077. }
  1078. if( i != dwPartitionCount )
  1079. {
  1080. // The starting offset of the next free space could be the end offset of the current member
  1081. // Always take the lower cylinder border greater or equal with this value
  1082. llFreeSpaceStartOffset = GetCylinderBorderAfter( arrPartEndOffset[ arrOrder[i] ] );
  1083. }
  1084. }
  1085. // We didn't find a matching free space
  1086. return FALSE;
  1087. MY_CATCH_AND_THROW
  1088. }
  1089. /*
  1090. Protected method: AddPartitionToTable
  1091. Purpose: Add a new container partition AND/OR a new non-container partition to m_pBuffer->PartitionInfo
  1092. table and make all necessary changes to the partition table
  1093. Parameters: [IN] LONGLONG llPartStartOffset
  1094. The starting offset of the new partition
  1095. [IN] LONGLONG llPartTrueSize
  1096. The size of the new partition
  1097. [IN] DWORD dwNewPartIndex
  1098. The index of the new partition in m_pBuffer->PartitionInfo
  1099. [IN] BOOL bCreateContainer
  1100. Should we create a new extended partition?
  1101. [IN] BOOL bCreateNonContainer
  1102. Should we create a new non-container partition?
  1103. Note: bCreateContainer and bNonCreateContainer can be both TRUE. Then a container partition
  1104. is created first and a non-container partition is created inside this container
  1105. These BOOL values cannot be both FALSE.
  1106. Return value: TRUE if the partition table was modified successfully
  1107. */
  1108. BOOL CDiskMap::AddPartitionToTable(
  1109. LONGLONG llPartStartOffset,
  1110. LONGLONG llPartSize,
  1111. DWORD dwNewPartIndex,
  1112. BOOL bCreateContainer,
  1113. BOOL bCreateNonContainer)
  1114. {
  1115. MY_TRY
  1116. ASSERT( m_bLoaded );
  1117. ASSERT( ( dwNewPartIndex >= 0 ) && ( dwNewPartIndex < m_pBuffer->PartitionCount ) );
  1118. // We can create a new container partition, a new non-container partition or a
  1119. // new non-container partition inside a new container partition
  1120. ASSERT( bCreateContainer || bCreateNonContainer );
  1121. // Get the 4 slots table of its parent
  1122. DWORD dwTableIndex = ((DWORD)( dwNewPartIndex / 4 )) * 4;
  1123. DWORD dwMemberCount = ( dwTableIndex + 4 <= m_pBuffer->PartitionCount ) ? 4 : m_pBuffer->PartitionCount - dwTableIndex;
  1124. PARTITION_INFORMATION* pTable = &( m_pBuffer->PartitionEntry[dwTableIndex] );
  1125. // 1. Get the chunks of m_pBuffer->PartitionEntry allocated for every container member of the extended partition
  1126. DWORD arrStartIndex[4];
  1127. DWORD arrEndIndex[4];
  1128. DWORD dwNextIndex = dwTableIndex + 4;
  1129. for( DWORD i = 0; i < dwMemberCount; i++ )
  1130. {
  1131. if( IsContainerPartition( pTable[i].PartitionType ) )
  1132. {
  1133. arrStartIndex[i] = dwNextIndex;
  1134. arrEndIndex[i] = dwNextIndex = GetNextIndex(dwNextIndex);
  1135. }
  1136. }
  1137. // 2. Sort the members of the extended partition by starting offset
  1138. DWORD arrOrder[4];
  1139. DWORD dwPartitionCount = SortPartitionsByOffset( pTable, dwMemberCount, arrOrder );
  1140. // There should be another free slot in the table. SearchForFreeSpaceInExtendedPartition should take care of this
  1141. ASSERT( dwPartitionCount < dwMemberCount );
  1142. // 3. Allocate a new buffer. We must be prepared to add some extra 4 slots to the partition table
  1143. PDRIVE_LAYOUT_INFORMATION pNewBuffer = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc( 0,
  1144. sizeof(DRIVE_LAYOUT_INFORMATION) + ( m_pBuffer->PartitionCount + 4 )*sizeof(PARTITION_INFORMATION) );
  1145. if( pNewBuffer == NULL )
  1146. {
  1147. AfxMessageBox( IDS_ERR_ALLOCATION, MB_ICONSTOP);
  1148. return FALSE;
  1149. }
  1150. // The drive info and the first m_dwTableIndex partition entries will not be changed in the new buffer
  1151. memcpy( pNewBuffer, m_pBuffer, sizeof(DRIVE_LAYOUT_INFORMATION) + dwTableIndex*sizeof(PARTITION_INFORMATION) );
  1152. // 4. Add the first ( dwNewPartIndex - dwTableIndex ) members of the extended partition to the new buffer
  1153. // in the starting offset order. Add also their chunks of m_pBuffer->PartitionEntry
  1154. PARTITION_INFORMATION* pNewTable = &( pNewBuffer->PartitionEntry[dwTableIndex] );
  1155. UINT iNext; // Index in pTable
  1156. UINT iNewNext = 0; // Index in pNewTable
  1157. DWORD dwNewNextIndex = dwTableIndex + 4; // Index in pNewBuffer->PartitionEntry of the next free chunk for an extended partition
  1158. for( iNext = 0, iNewNext = 0; iNext < dwNewPartIndex - dwTableIndex; iNext++, iNewNext++ )
  1159. {
  1160. DWORD iOrder = arrOrder[iNext];
  1161. memcpy( &(pNewTable[iNewNext]), &(pTable[iOrder]), sizeof(PARTITION_INFORMATION));
  1162. if( iNewNext != iOrder )
  1163. pNewTable[iNewNext].RewritePartition = TRUE;
  1164. if( IsContainerPartition( pNewTable[iNewNext].PartitionType ) )
  1165. {
  1166. memcpy( &(pNewBuffer->PartitionEntry[dwNewNextIndex]),
  1167. &(m_pBuffer->PartitionEntry[ arrStartIndex[iOrder] ]),
  1168. ( arrEndIndex[iOrder] - arrStartIndex[iOrder] ) * sizeof(PARTITION_INFORMATION));
  1169. if( arrStartIndex[iOrder] != dwNewNextIndex )
  1170. {
  1171. for( DWORD j = arrStartIndex[iOrder]; j < arrEndIndex[iOrder]; j++ )
  1172. pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
  1173. }
  1174. else
  1175. dwNewNextIndex += ( arrEndIndex[iOrder] - arrStartIndex[iOrder] );
  1176. }
  1177. }
  1178. // 5. Add the partition ( or a container if required ) in the slot dwNewPartIndex
  1179. FillNewPartitionInfo( llPartStartOffset, llPartSize,
  1180. &(pNewTable[iNewNext++]), bCreateContainer );
  1181. if( bCreateContainer )
  1182. {
  1183. // 5.1 Add a new 4 slots table for the new container
  1184. pNewBuffer->PartitionCount += 4;
  1185. PARTITION_INFORMATION* pContainerTable = &( pNewBuffer->PartitionEntry[dwNewNextIndex] );
  1186. dwNewNextIndex += 4;
  1187. UINT iContainerNext = 0;
  1188. // 5.2 Create the non-container partition in the first slot
  1189. if( bCreateNonContainer )
  1190. FillNewPartitionInfo( llPartStartOffset + m_llTrackSize, llPartSize - m_llTrackSize,
  1191. &(pContainerTable[iContainerNext++]), FALSE );
  1192. if( dwTableIndex == 0 )
  1193. {
  1194. // The new container is among primary partitions
  1195. // 5.3 Fill with zeroes the last entries of the new container table
  1196. for( ; iContainerNext < 4; iContainerNext++ )
  1197. {
  1198. memset( &(pContainerTable[iContainerNext]), 0, sizeof(PARTITION_INFORMATION) );
  1199. pContainerTable[iContainerNext].RewritePartition = TRUE;
  1200. }
  1201. // All following primary partitions will be added to pNewTable after this new container
  1202. }
  1203. else
  1204. {
  1205. // The new container is inside another container
  1206. // 5.3 Fill with zeroes the last entries of the table pNewTable
  1207. for( ; iNewNext < 4; iNewNext++ )
  1208. {
  1209. memset( &(pNewTable[iNewNext]), 0, sizeof(PARTITION_INFORMATION));
  1210. pNewTable[iNewNext].RewritePartition = TRUE;
  1211. }
  1212. // 5.4. The new table becomes the table of the newly created container
  1213. // All following partitions of the parent container will be added to the new container
  1214. pNewTable = pContainerTable;
  1215. iNewNext = iContainerNext;
  1216. }
  1217. }
  1218. // 6. Copy the rest of valid partitions from pTable to pNewTable ( also ordered by starting offset )
  1219. for( ; iNext < dwPartitionCount; iNext++, iNewNext++ )
  1220. {
  1221. DWORD iOrder = arrOrder[iNext];
  1222. memcpy( &(pNewTable[iNewNext]), &(pTable[iOrder]), sizeof(PARTITION_INFORMATION));
  1223. pNewTable[iNewNext].RewritePartition = TRUE;
  1224. if( IsContainerPartition( pNewTable[iNewNext].PartitionType ) )
  1225. {
  1226. memcpy( &(pNewBuffer->PartitionEntry[dwNewNextIndex]),
  1227. &(m_pBuffer->PartitionEntry[ arrStartIndex[iOrder] ]),
  1228. ( arrEndIndex[iOrder] - arrStartIndex[iOrder] ) * sizeof(PARTITION_INFORMATION));
  1229. if( arrStartIndex[iOrder] != dwNewNextIndex )
  1230. {
  1231. for( DWORD j = arrStartIndex[iOrder]; j < arrEndIndex[iOrder]; j++ )
  1232. pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
  1233. }
  1234. else
  1235. dwNewNextIndex += ( arrEndIndex[iOrder] - arrStartIndex[iOrder] );
  1236. }
  1237. }
  1238. // 7. Fill with zeroes the last entries of pNewTable
  1239. for( ; iNewNext < 4; iNewNext++ )
  1240. {
  1241. memset( &(pNewTable[iNewNext]), 0, sizeof(PARTITION_INFORMATION));
  1242. pNewTable[iNewNext].RewritePartition = TRUE;
  1243. }
  1244. // 8. Add the tail of m_pBuffer to the tail of pNewBuffer
  1245. if( bCreateContainer )
  1246. ASSERT( dwNewNextIndex == dwNextIndex + 4 );
  1247. else
  1248. ASSERT( dwNewNextIndex == dwNextIndex );
  1249. memcpy( &( pNewBuffer->PartitionEntry[dwNewNextIndex] ),
  1250. &( m_pBuffer->PartitionEntry[dwNextIndex] ),
  1251. ( m_pBuffer->PartitionCount - dwNextIndex ) * sizeof(PARTITION_INFORMATION) );
  1252. if( dwNewNextIndex != dwNextIndex )
  1253. {
  1254. for( DWORD j = dwNextIndex; j < m_pBuffer->PartitionCount; j++ )
  1255. pNewBuffer->PartitionEntry[dwNewNextIndex++].RewritePartition = TRUE;
  1256. }
  1257. else
  1258. dwNewNextIndex += ( m_pBuffer->PartitionCount - dwNextIndex );
  1259. ASSERT( dwNewNextIndex == pNewBuffer->PartitionCount );
  1260. // 9. Replace the old m_pBuffer with the new buffer
  1261. LocalFree( m_pBuffer );
  1262. m_pBuffer = pNewBuffer;
  1263. return TRUE;
  1264. MY_CATCH_AND_THROW
  1265. }
  1266. /*
  1267. Protected method: FillNewPartitionInfo
  1268. Purpose: Fill a PARTITION_INFORMATION structure with the info of a new partition
  1269. Parameters: [IN] LONGLONG llPartStartOffset
  1270. The starting offset of the new partition
  1271. [IN] LONGLONG llPartSize
  1272. The size of the new partition
  1273. [OUT] PARTITION_INFORMATION*
  1274. Pointer to the structure to fill
  1275. [IN] BOOL bContainer
  1276. Is the new partition a container?
  1277. Return value: -
  1278. */
  1279. void CDiskMap::FillNewPartitionInfo( LONGLONG llPartStartOffset, LONGLONG llPartSize,/* LONGLONG llExtPartStartOffset, */
  1280. PARTITION_INFORMATION* pPartInfo, BOOL bContainer )
  1281. {
  1282. MY_TRY
  1283. ASSERT( m_bLoaded );
  1284. ASSERT( pPartInfo );
  1285. pPartInfo->StartingOffset.QuadPart = llPartStartOffset;
  1286. pPartInfo->PartitionLength.QuadPart = llPartSize;
  1287. //#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?")
  1288. pPartInfo->HiddenSectors = (DWORD)( ( llPartStartOffset ) / m_llSectorSize);
  1289. // Don't assign any particular number for the partition. The system will take care of that
  1290. pPartInfo->PartitionNumber = 0;
  1291. pPartInfo->BootIndicator = FALSE;
  1292. pPartInfo->RewritePartition = TRUE;
  1293. if( bContainer )
  1294. {
  1295. pPartInfo->PartitionType = PARTITION_EXTENDED;
  1296. pPartInfo->RecognizedPartition = FALSE;
  1297. }
  1298. else
  1299. {
  1300. //Windisk and Disk Management don't care about the number of sectors of the partition
  1301. //They always create partitions with the type PARTITION_HUGE
  1302. /*
  1303. LONGLONG llSectorCount = ( llPartSize + m_llSectorSize - 1 ) / m_llSectorSize;
  1304. if( llSectorCount <= CSEC_FAT )
  1305. pPartInfo->PartitionType = PARTITION_FAT_12;
  1306. else if( llSectorCount <= CSEC_FAT32MEG )
  1307. pPartInfo->PartitionType = PARTITION_FAT_16;
  1308. else
  1309. pPartInfo->PartitionType = PARTITION_HUGE;
  1310. */
  1311. pPartInfo->PartitionType = PARTITION_HUGE;
  1312. pPartInfo->RecognizedPartition = TRUE;
  1313. }
  1314. MY_CATCH_AND_THROW
  1315. }
  1316. /*
  1317. Protected method: AddError
  1318. Purpose: Add a error message to a string .The error message will be formatted like this:
  1319. <Disk number: >< My error message > [ System error message ]
  1320. Parameters: [IN/OUT] CString& strErrors
  1321. The string that must be appended with the error message
  1322. [IN] UINT unErrorMsg
  1323. Our error message. ID of a string from resources
  1324. [IN] BOOL bAddSystemMsg
  1325. TRUE if the latest error system message must be added to our message
  1326. Return value: -
  1327. */
  1328. void CDiskMap::AddError(
  1329. CString& strErrors,
  1330. UINT unErrorMsg,
  1331. BOOL bAddSystemMsg /* =FALSE */ )
  1332. {
  1333. MY_TRY
  1334. CString str, strName, strErr, strSystemErr;
  1335. // Get system error message
  1336. if( bAddSystemMsg )
  1337. {
  1338. LPVOID lpMsgBuf;
  1339. if( ::FormatMessage(
  1340. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1341. NULL,
  1342. GetLastError(),
  1343. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  1344. (LPTSTR) &lpMsgBuf,
  1345. 0,
  1346. NULL ) ) // Process any inserts in lpMsgBuf.
  1347. {
  1348. strSystemErr = (LPCTSTR)lpMsgBuf;
  1349. LocalFree( lpMsgBuf );
  1350. }
  1351. }
  1352. strName.Format( IDS_DISK, m_dwDiskNumber );
  1353. // Get my error message
  1354. strErr.LoadString(unErrorMsg);
  1355. str.Format(_T("%s: %s %s\n"), strName, strErr, strSystemErr );
  1356. strErrors += str;
  1357. MY_CATCH_AND_THROW
  1358. }