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.

2514 lines
76 KiB

  1. /*****************************************************************************************************************
  2. FILENAME: FreeSpac.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. DESCRIPTION:
  5. Contains routines for finding free clusters using the volume's bitmap.
  6. */
  7. #include "stdafx.h"
  8. #ifdef OFFLINEDK
  9. extern "C"{
  10. #include <stdio.h>
  11. }
  12. #endif
  13. #ifdef BOOTIME
  14. #include "Offline.h"
  15. #else
  16. #include "Windows.h"
  17. #endif
  18. #include <winioctl.h>
  19. #include <math.h>
  20. extern "C" {
  21. #include "SysStruc.h"
  22. }
  23. #include "ErrMacro.h"
  24. #include "DfrgCmn.h"
  25. #include "DfrgEngn.h"
  26. #include "DfrgRes.h"
  27. #include "GetDfrgRes.h"
  28. #include "GetReg.h"
  29. #include "Devio.h"
  30. #include "FreeSpace.h"
  31. #include "Alloc.h"
  32. #include "Message.h"
  33. #define THIS_MODULE 'F'
  34. #include "logfile.h"
  35. // macros to speed up LONGLONG math (multiplication and division)
  36. // based on profiling data by Mark Patton
  37. // set OPTLONGLONGMATH as follows:
  38. // 1=optimize with shift and &
  39. // 0=use / and %
  40. #define OPTLONGLONGMATH 1
  41. #if OPTLONGLONGMATH
  42. #define DIVIDELONGLONGBY32(num) Int64ShraMod32((num), 5)
  43. #define MODULUSLONGLONGBY32(num) ((num) & 0x1F)
  44. #else
  45. #define DIVIDELONGLONGBY32(num) ((num) / 32)
  46. #define MODULUSLONGLONGBY32(num) ((num) % 32)
  47. #endif
  48. //
  49. // This structure is the header for a generic table entry.
  50. // Align this structure on a 8 byte boundary so the user
  51. // data is correctly aligned.
  52. //
  53. typedef struct _TABLE_ENTRY_HEADER {
  54. RTL_BALANCED_LINKS BalancedLinks;
  55. LONGLONG UserData;
  56. } TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER;
  57. PVOID
  58. RealPredecessor (
  59. IN PRTL_BALANCED_LINKS Links
  60. )
  61. /*++
  62. Routine Description:
  63. The RealPredecessor function takes as input a pointer to a balanced link
  64. in a tree and returns a pointer to the predecessor of the input node
  65. within the entire tree. If there is not a predecessor, the return value
  66. is NULL.
  67. Arguments:
  68. Links - Supplies a pointer to a balanced link in a tree.
  69. Return Value:
  70. PRTL_BALANCED_LINKS - returns a pointer to the predecessor in the entire tree
  71. --*/
  72. {
  73. PRTL_BALANCED_LINKS Ptr;
  74. /*
  75. first check to see if there is a left subtree to the input link
  76. if there is then the real predecessor is the right most node in
  77. the left subtree. That is find and return P in the following diagram
  78. Links
  79. /
  80. .
  81. .
  82. .
  83. P
  84. /
  85. */
  86. if ((Ptr = Links->LeftChild) != NULL) {
  87. while (Ptr->RightChild != NULL) {
  88. Ptr = Ptr->RightChild;
  89. }
  90. return ((PVOID)&((PTABLE_ENTRY_HEADER)Ptr)->UserData);
  91. }
  92. /*
  93. we do not have a left child so check to see if have a parent and if
  94. so find the first ancestor that we are a right decendent of. That
  95. is find and return P in the following diagram
  96. P
  97. \
  98. .
  99. .
  100. .
  101. Links
  102. Note that this code depends on how the BalancedRoot is initialized, which is
  103. Parent points to self, and the RightChild points to an actual node which is
  104. the root of the tree.
  105. */
  106. Ptr = Links;
  107. while (RtlIsLeftChild(Ptr)) {
  108. Ptr = Ptr->Parent;
  109. }
  110. if (RtlIsRightChild(Ptr) && (Ptr->Parent->Parent != Ptr->Parent)) {
  111. return ((PVOID)&((PTABLE_ENTRY_HEADER)(Ptr->Parent))->UserData);
  112. }
  113. //
  114. // otherwise we are do not have a real predecessor so we simply return
  115. // NULL
  116. //
  117. return NULL;
  118. }
  119. PVOID
  120. PreviousEntry(
  121. IN PVOID pCurrentEntry
  122. )
  123. {
  124. if (!pCurrentEntry) {
  125. return NULL;
  126. }
  127. PTABLE_ENTRY_HEADER q = (PTABLE_ENTRY_HEADER) CONTAINING_RECORD(
  128. pCurrentEntry,
  129. TABLE_ENTRY_HEADER,
  130. UserData);
  131. return RealPredecessor(&(q->BalancedLinks));
  132. }
  133. PVOID
  134. LastEntry(
  135. IN PRTL_AVL_TABLE Table
  136. )
  137. {
  138. if (!Table) {
  139. return NULL;
  140. }
  141. PRTL_BALANCED_LINKS NodeToExamine = Table->BalancedRoot.RightChild;
  142. PRTL_BALANCED_LINKS Child;
  143. while (Child = NodeToExamine->RightChild) {
  144. NodeToExamine = Child;
  145. }
  146. if (NodeToExamine) {
  147. return ((PVOID)&((PTABLE_ENTRY_HEADER)NodeToExamine)->UserData);
  148. }
  149. else {
  150. return NULL;
  151. }
  152. }
  153. #if 0
  154. void
  155. DumpFreeSpace(
  156. IN PRTL_GENERIC_TABLE pTable
  157. )
  158. {
  159. PFREE_SPACE_ENTRY pFreeSpaceEntry;
  160. BOOLEAN bNext = FALSE;
  161. WCHAR szTemp[256];
  162. PVOID pRestartKey = NULL;
  163. ULONG ulDeleteCount = 0;
  164. int iCount = 0;
  165. FREE_SPACE_ENTRY entry;
  166. ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
  167. do {
  168. pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
  169. pTable,
  170. NULL,
  171. NULL,
  172. bNext,
  173. &pRestartKey,
  174. &ulDeleteCount,
  175. &entry);
  176. bNext = TRUE;
  177. if (!pFreeSpaceEntry) {
  178. // No free space left
  179. OutputDebugString(L"No free space left\n\n");
  180. break;
  181. }
  182. wsprintf(szTemp, L">> Free space \t Start:%I64u, \t ClusterCount:%I64u\n",
  183. pFreeSpaceEntry->StartingLcn,
  184. pFreeSpaceEntry->ClusterCount);
  185. OutputDebugString(szTemp);
  186. } while ((pFreeSpaceEntry) && (++iCount < 20));
  187. }
  188. #endif
  189. BOOL
  190. BuildFreeSpaceList(
  191. IN OUT PRTL_GENERIC_TABLE pFreeSpaceTable,
  192. IN CONST LONGLONG MinClusterCount,
  193. IN CONST BOOL bSortBySize,
  194. OUT LONGLONG *pBiggestFreeSpaceClusterCount,
  195. OUT LONGLONG *pBiggestFreeSpaceStartingLcn,
  196. IN CONST BOOL bIgnoreMftZone
  197. )
  198. {
  199. LONGLONG Lcn = 0,
  200. StartingLcn = 0,
  201. EndingLcn = 0,
  202. ClusterCount = 0;
  203. FREE_SPACE_ENTRY FreeSpaceEntry;
  204. PVOID p = NULL;
  205. BOOL bResult = TRUE;
  206. BOOLEAN bNewElement = FALSE;
  207. PULONG pBitmap = NULL;
  208. GetVolumeBitmap();
  209. //0.0E00 Get a pointer to the free space bitmap
  210. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  211. if (!pVolumeBitmap){
  212. Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
  213. return FALSE; // no need to Unlock--the lock failed
  214. }
  215. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  216. //0.0E00 Scan thru the entire bitmap looking for free space extents
  217. for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
  218. //0.0E00 Find the next free space extent
  219. FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
  220. if (0 == ClusterCount) {
  221. // No more free spaces, we're done
  222. break;
  223. }
  224. if (ClusterCount < MinClusterCount) {
  225. continue;
  226. }
  227. EndingLcn = StartingLcn + ClusterCount;
  228. //0.0E00 If NTFS clip the free space extent to exclude the MFT zone
  229. if(VolData.FileSystem == FS_NTFS) {
  230. if (!bIgnoreMftZone) {
  231. if((StartingLcn < VolData.MftZoneEnd) &&
  232. (EndingLcn > VolData.MftZoneStart)) {
  233. if(StartingLcn < VolData.MftZoneStart) {
  234. EndingLcn = VolData.MftZoneStart;
  235. }
  236. else if(EndingLcn <= VolData.MftZoneEnd) {
  237. continue; // this zone is fully within the MFT zone
  238. }
  239. else {
  240. //0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
  241. StartingLcn = VolData.MftZoneEnd;
  242. }
  243. }
  244. }
  245. if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
  246. (EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
  247. if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
  248. EndingLcn = VolData.BootOptimizeBeginClusterExclude;
  249. }
  250. else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
  251. continue; // this zone is fully within the boot-opt zone
  252. }
  253. else {
  254. //0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
  255. StartingLcn = VolData.BootOptimizeEndClusterExclude;
  256. }
  257. }
  258. }
  259. FreeSpaceEntry.StartingLcn = StartingLcn;
  260. FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
  261. FreeSpaceEntry.SortBySize = bSortBySize;
  262. if (pBiggestFreeSpaceClusterCount) {
  263. if (FreeSpaceEntry.ClusterCount > *pBiggestFreeSpaceClusterCount) {
  264. *pBiggestFreeSpaceClusterCount = FreeSpaceEntry.ClusterCount;
  265. if (pBiggestFreeSpaceStartingLcn) {
  266. *pBiggestFreeSpaceStartingLcn = StartingLcn;
  267. }
  268. }
  269. }
  270. p = RtlInsertElementGenericTable(
  271. pFreeSpaceTable,
  272. (PVOID) &FreeSpaceEntry,
  273. sizeof(FREE_SPACE_ENTRY),
  274. &bNewElement);
  275. if (!p) {
  276. //
  277. // An allocation failed
  278. //
  279. bResult = FALSE;
  280. break;
  281. };
  282. }
  283. if (VolData.hVolumeBitmap) {
  284. GlobalUnlock(VolData.hVolumeBitmap);
  285. }
  286. //DumpFreeSpace(pFreeSpaceTable);
  287. return bResult;
  288. }
  289. BOOL
  290. BuildFreeSpaceListWithExclude(
  291. IN OUT PRTL_GENERIC_TABLE pFreeSpaceTable,
  292. IN CONST LONGLONG MinClusterCount,
  293. IN CONST LONGLONG ExcludeZoneStart,
  294. IN CONST LONGLONG ExcludeZoneEnd,
  295. IN CONST BOOL bSortBySize,
  296. IN CONST BOOL bExcludeMftZone
  297. )
  298. {
  299. LONGLONG Lcn = 0,
  300. StartingLcn = 0,
  301. EndingLcn = 0,
  302. ClusterCount = 0;
  303. FREE_SPACE_ENTRY FreeSpaceEntry;
  304. PVOID p = NULL;
  305. BOOL bResult = TRUE;
  306. BOOLEAN bNewElement = FALSE;
  307. PULONG pBitmap = NULL;
  308. GetVolumeBitmap();
  309. //0.0E00 Get a pointer to the free space bitmap
  310. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  311. if (!pVolumeBitmap){
  312. Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
  313. return FALSE; // no need to Unlock--the lock failed
  314. }
  315. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  316. //0.0E00 Scan thru the entire bitmap looking for free space extents
  317. for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
  318. //0.0E00 Find the next free space extent
  319. FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
  320. if (0 == ClusterCount) {
  321. // No more free spaces, we're done
  322. break;
  323. }
  324. if (ClusterCount < MinClusterCount) {
  325. continue;
  326. }
  327. EndingLcn = StartingLcn + ClusterCount;
  328. //0.0E00 If NTFS clip the free space extent to exclude the MFT zone
  329. if ((bExcludeMftZone) && (VolData.FileSystem == FS_NTFS)) {
  330. if((StartingLcn < VolData.MftZoneEnd) &&
  331. (EndingLcn > VolData.MftZoneStart)) {
  332. if(StartingLcn < VolData.MftZoneStart) {
  333. EndingLcn = VolData.MftZoneStart;
  334. }
  335. else if(EndingLcn <= VolData.MftZoneEnd) {
  336. continue; // this zone is fully within the MFT zone
  337. }
  338. else {
  339. //0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
  340. StartingLcn = VolData.MftZoneEnd;
  341. }
  342. }
  343. if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
  344. (EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
  345. if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
  346. EndingLcn = VolData.BootOptimizeBeginClusterExclude;
  347. }
  348. else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
  349. continue; // this zone is fully within the boot-opt zone
  350. }
  351. else {
  352. //0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
  353. StartingLcn = VolData.BootOptimizeEndClusterExclude;
  354. }
  355. }
  356. }
  357. if((StartingLcn < ExcludeZoneEnd) &&
  358. (EndingLcn > ExcludeZoneStart)) {
  359. if(StartingLcn < ExcludeZoneStart) {
  360. EndingLcn = ExcludeZoneStart;
  361. }
  362. else if(EndingLcn <= ExcludeZoneEnd) {
  363. continue; // this zone is fully within the MFT zone
  364. }
  365. else {
  366. //0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
  367. StartingLcn = ExcludeZoneEnd;
  368. }
  369. }
  370. FreeSpaceEntry.StartingLcn = StartingLcn;
  371. FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
  372. FreeSpaceEntry.SortBySize = bSortBySize;
  373. p = RtlInsertElementGenericTable(
  374. pFreeSpaceTable,
  375. (PVOID) &FreeSpaceEntry,
  376. sizeof(FREE_SPACE_ENTRY),
  377. &bNewElement);
  378. if (!p) {
  379. //
  380. // An allocation failed
  381. //
  382. bResult = FALSE;
  383. break;
  384. };
  385. }
  386. if (VolData.hVolumeBitmap) {
  387. GlobalUnlock(VolData.hVolumeBitmap);
  388. }
  389. return bResult;
  390. }
  391. BOOL
  392. BuildFreeSpaceListWithMultipleTrees(
  393. OUT LONGLONG *pBiggestFreeSpaceClusterCount,
  394. IN CONST LONGLONG IncludeZoneStartingLcn,
  395. IN CONST LONGLONG IncludeZoneEndingLcn
  396. )
  397. {
  398. LONGLONG Lcn = 0,
  399. StartingLcn = 0,
  400. EndingLcn = 0,
  401. ClusterCount = 0;
  402. FREE_SPACE_ENTRY FreeSpaceEntry;
  403. PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
  404. PVOID p = NULL;
  405. BOOL bResult = TRUE;
  406. BOOLEAN bNewElement = FALSE;
  407. PULONG pBitmap = NULL;
  408. DWORD dwTableIndex = 0;
  409. GetVolumeBitmap();
  410. //0.0E00 Get a pointer to the free space bitmap
  411. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  412. if (!pVolumeBitmap){
  413. Trace(warn, "BuildFreeSpaceList. Unable to get VolumeBitmap");
  414. return FALSE; // no need to Unlock--the lock failed
  415. }
  416. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  417. //0.0E00 Scan thru the entire bitmap looking for free space extents
  418. for(Lcn = 0; Lcn < VolData.TotalClusters; ) {
  419. //0.0E00 Find the next free space extent
  420. FindFreeExtent(pBitmap, VolData.TotalClusters, &Lcn, &StartingLcn, &ClusterCount);
  421. if (0 == ClusterCount) {
  422. // No more free spaces, we're done
  423. break;
  424. }
  425. EndingLcn = StartingLcn + ClusterCount;
  426. //0.0E00 If NTFS clip the free space extent to exclude the MFT zone
  427. if ((VolData.FileSystem == FS_NTFS) && (0 == IncludeZoneEndingLcn)) {
  428. if((StartingLcn < VolData.MftZoneEnd) &&
  429. (EndingLcn > VolData.MftZoneStart)) {
  430. if(StartingLcn < VolData.MftZoneStart) {
  431. EndingLcn = VolData.MftZoneStart;
  432. }
  433. else if(EndingLcn <= VolData.MftZoneEnd) {
  434. continue; // this zone is fully within the MFT zone
  435. }
  436. else {
  437. //0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
  438. StartingLcn = VolData.MftZoneEnd;
  439. }
  440. }
  441. if((StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
  442. (EndingLcn > VolData.BootOptimizeBeginClusterExclude)) {
  443. if(StartingLcn < VolData.BootOptimizeBeginClusterExclude) {
  444. EndingLcn = VolData.BootOptimizeBeginClusterExclude;
  445. }
  446. else if(EndingLcn <= VolData.BootOptimizeEndClusterExclude) {
  447. continue; // this zone is fully within the boot-opt zone
  448. }
  449. else {
  450. //0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
  451. StartingLcn = VolData.BootOptimizeEndClusterExclude;
  452. }
  453. }
  454. }
  455. //
  456. // Trim it down to the interesting zone. If this zone starts beyond
  457. // the zone we're interested in, or ends before it, we ignore it.
  458. //
  459. if (IncludeZoneEndingLcn) {
  460. if ((StartingLcn < IncludeZoneEndingLcn) &&
  461. (EndingLcn > IncludeZoneStartingLcn)) {
  462. if (StartingLcn < IncludeZoneStartingLcn) {
  463. StartingLcn = IncludeZoneStartingLcn;
  464. }
  465. if (EndingLcn > IncludeZoneEndingLcn) {
  466. EndingLcn = IncludeZoneEndingLcn;
  467. }
  468. }
  469. else {
  470. continue;
  471. }
  472. }
  473. FreeSpaceEntry.StartingLcn = StartingLcn;
  474. FreeSpaceEntry.ClusterCount = EndingLcn - StartingLcn;
  475. FreeSpaceEntry.SortBySize = FALSE;
  476. if (pBiggestFreeSpaceClusterCount) {
  477. if (FreeSpaceEntry.ClusterCount > *pBiggestFreeSpaceClusterCount) {
  478. *pBiggestFreeSpaceClusterCount = FreeSpaceEntry.ClusterCount;
  479. }
  480. }
  481. //
  482. // Add this entry to the appropriate table. We have ten free space
  483. // tables, and they contain entries of the following sizes:
  484. //
  485. // dwTableIndex Free space size
  486. // ------------ ---------------
  487. // 0 1
  488. // 1 2
  489. // 2 3-4
  490. // 3 5-8
  491. // 4 9-16
  492. // 5 17-32
  493. // 6 33-64
  494. // 7 65-256
  495. // 8 257-4096
  496. // 9 4097+
  497. //
  498. //
  499. // Entries in each table are sorted by StartingLcn.
  500. //
  501. if (FreeSpaceEntry.ClusterCount <= 1) {
  502. dwTableIndex = 0;
  503. }
  504. else if (FreeSpaceEntry.ClusterCount == 2) {
  505. dwTableIndex = 1;
  506. }
  507. else if (FreeSpaceEntry.ClusterCount <= 4) {
  508. dwTableIndex = 2;
  509. }
  510. else if (FreeSpaceEntry.ClusterCount <= 8) {
  511. dwTableIndex = 3;
  512. }
  513. else if (FreeSpaceEntry.ClusterCount <= 16) {
  514. dwTableIndex = 4;
  515. }
  516. else if (FreeSpaceEntry.ClusterCount <= 32) {
  517. dwTableIndex = 5;
  518. }
  519. else if (FreeSpaceEntry.ClusterCount <= 64) {
  520. dwTableIndex = 6;
  521. }
  522. else if (FreeSpaceEntry.ClusterCount <= 256) {
  523. dwTableIndex = 7;
  524. }
  525. else if (FreeSpaceEntry.ClusterCount <= 4096) {
  526. dwTableIndex = 8;
  527. }
  528. else {
  529. dwTableIndex = 9;
  530. }
  531. pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
  532. p = RtlInsertElementGenericTable(
  533. pFreeSpaceTable,
  534. (PVOID) &FreeSpaceEntry,
  535. sizeof(FREE_SPACE_ENTRY),
  536. &bNewElement);
  537. if (!p) {
  538. //
  539. // An allocation failed
  540. //
  541. bResult = FALSE;
  542. break;
  543. };
  544. }
  545. if (VolData.hVolumeBitmap) {
  546. GlobalUnlock(VolData.hVolumeBitmap);
  547. }
  548. return bResult;
  549. }
  550. BOOL
  551. FindFreeSpaceWithMultipleTrees(
  552. IN CONST LONGLONG ClusterCount,
  553. IN CONST LONGLONG MaxStartingLcn
  554. )
  555. {
  556. PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
  557. DWORD dwTableIndex = 0;
  558. BOOL done = FALSE;
  559. PFREE_SPACE_ENTRY pFreeSpaceEntry;
  560. BOOLEAN bRestart = TRUE;
  561. LONGLONG CurrentBest = VolData.TotalClusters;
  562. //
  563. // Find out which table we want to start our search from. We have ten free
  564. // space tables, and they contain entries of the following sizes:
  565. //
  566. // dwTableIndex Free space size
  567. // ------------ ---------------
  568. // 0 1
  569. // 1 2
  570. // 2 3-4
  571. // 3 5-8
  572. // 4 9-16
  573. // 5 17-32
  574. // 6 33-64
  575. // 7 65-256
  576. // 8 257-4096
  577. // 9 4097+
  578. //
  579. //
  580. // Entries in each table are sorted by StartingLcn.
  581. //
  582. if (ClusterCount <= 1) {
  583. dwTableIndex = 0;
  584. }
  585. else if (ClusterCount == 2) {
  586. dwTableIndex = 1;
  587. }
  588. else if (ClusterCount <= 4) {
  589. dwTableIndex = 2;
  590. }
  591. else if (ClusterCount <= 8) {
  592. dwTableIndex = 3;
  593. }
  594. else if (ClusterCount <= 16) {
  595. dwTableIndex = 4;
  596. }
  597. else if (ClusterCount <= 32) {
  598. dwTableIndex = 5;
  599. }
  600. else if (ClusterCount <= 64) {
  601. dwTableIndex = 6;
  602. }
  603. else if (ClusterCount <= 256) {
  604. dwTableIndex = 7;
  605. }
  606. else if (ClusterCount <= 4096) {
  607. dwTableIndex = 8;
  608. }
  609. else {
  610. dwTableIndex = 9;
  611. }
  612. // Assume no free space left
  613. VolData.pFreeSpaceEntry = NULL;
  614. VolData.FoundLcn = VolData.TotalClusters;
  615. VolData.FoundLen = 0;
  616. //
  617. // Search through the entries in this table, till we come across the first
  618. // entry that is big enough.
  619. //
  620. pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
  621. do {
  622. pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeSpaceTable, bRestart);
  623. bRestart = FALSE;
  624. if ((!pFreeSpaceEntry) || (pFreeSpaceEntry->StartingLcn > MaxStartingLcn)) {
  625. break;
  626. }
  627. if (pFreeSpaceEntry->ClusterCount >= ClusterCount) {
  628. VolData.pFreeSpaceEntry = pFreeSpaceEntry;
  629. VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
  630. VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
  631. CurrentBest = pFreeSpaceEntry->StartingLcn;
  632. break;
  633. }
  634. } while (TRUE);
  635. //
  636. // For the remaining tables, search through to see if any of them
  637. // have entries that start before our current best.
  638. //
  639. while (++dwTableIndex < 10) {
  640. pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
  641. pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeSpaceTable, TRUE);
  642. if ((pFreeSpaceEntry) &&
  643. (pFreeSpaceEntry->ClusterCount >= ClusterCount) &&
  644. (pFreeSpaceEntry->StartingLcn < MaxStartingLcn) &&
  645. (pFreeSpaceEntry->StartingLcn < CurrentBest)
  646. ) {
  647. VolData.pFreeSpaceEntry = pFreeSpaceEntry;
  648. VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
  649. VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
  650. CurrentBest = pFreeSpaceEntry->StartingLcn;
  651. }
  652. }
  653. if (VolData.pFreeSpaceEntry) {
  654. return TRUE;
  655. }
  656. else {
  657. return FALSE;
  658. }
  659. }
  660. BOOL
  661. UpdateInMultipleTrees(
  662. IN PFREE_SPACE_ENTRY pOldEntry,
  663. IN PFREE_SPACE_ENTRY pNewEntry
  664. )
  665. {
  666. PRTL_GENERIC_TABLE pFreeSpaceTable = NULL;
  667. PVOID p = NULL;
  668. BOOL bResult = TRUE;
  669. BOOLEAN bNewElement = FALSE;
  670. LONGLONG ClusterCount = pOldEntry->ClusterCount;
  671. DWORD dwTableIndex = 0;
  672. //
  673. // Find out which table we want to delete from. We have ten free
  674. // space tables, and they contain entries of the following sizes:
  675. //
  676. // dwTableIndex Free space size
  677. // ------------ ---------------
  678. // 0 1
  679. // 1 2
  680. // 2 3-4
  681. // 3 5-8
  682. // 4 9-16
  683. // 5 17-32
  684. // 6 33-64
  685. // 7 65-256
  686. // 8 257-4096
  687. // 9 4097+
  688. //
  689. //
  690. // Entries in each table are sorted by StartingLcn.
  691. //
  692. if (ClusterCount <= 1) {
  693. dwTableIndex = 0;
  694. }
  695. else if (ClusterCount == 2) {
  696. dwTableIndex = 1;
  697. }
  698. else if (ClusterCount <= 4) {
  699. dwTableIndex = 2;
  700. }
  701. else if (ClusterCount <= 8) {
  702. dwTableIndex = 3;
  703. }
  704. else if (ClusterCount <= 16) {
  705. dwTableIndex = 4;
  706. }
  707. else if (ClusterCount <= 32) {
  708. dwTableIndex = 5;
  709. }
  710. else if (ClusterCount <= 64) {
  711. dwTableIndex = 6;
  712. }
  713. else if (ClusterCount <= 256) {
  714. dwTableIndex = 7;
  715. }
  716. else if (ClusterCount <= 4096) {
  717. dwTableIndex = 8;
  718. }
  719. else {
  720. dwTableIndex = 9;
  721. }
  722. pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
  723. bNewElement = RtlDeleteElementGenericTable(pFreeSpaceTable, (PVOID)pOldEntry);
  724. if (!bNewElement) {
  725. Trace(warn, "Could not find Element in Free Space Table!");
  726. assert(FALSE);
  727. bResult = FALSE;
  728. }
  729. //
  730. // Find out which table we want to add to.
  731. //
  732. if (pNewEntry) {
  733. ClusterCount = pNewEntry->ClusterCount;
  734. if (ClusterCount > 0) {
  735. if (ClusterCount <= 1) {
  736. dwTableIndex = 0;
  737. }
  738. else if (ClusterCount == 2) {
  739. dwTableIndex = 1;
  740. }
  741. else if (ClusterCount <= 4) {
  742. dwTableIndex = 2;
  743. }
  744. else if (ClusterCount <= 8) {
  745. dwTableIndex = 3;
  746. }
  747. else if (ClusterCount <= 16) {
  748. dwTableIndex = 4;
  749. }
  750. else if (ClusterCount <= 32) {
  751. dwTableIndex = 5;
  752. }
  753. else if (ClusterCount <= 64) {
  754. dwTableIndex = 6;
  755. }
  756. else if (ClusterCount <= 256) {
  757. dwTableIndex = 7;
  758. }
  759. else if (ClusterCount <= 4096) {
  760. dwTableIndex = 8;
  761. }
  762. else {
  763. dwTableIndex = 9;
  764. }
  765. pFreeSpaceTable = &VolData.MultipleFreeSpaceTrees[dwTableIndex];
  766. p = RtlInsertElementGenericTable(
  767. pFreeSpaceTable,
  768. (PVOID) pNewEntry,
  769. sizeof(FREE_SPACE_ENTRY),
  770. &bNewElement);
  771. if (!p) {
  772. //
  773. // An allocation failed
  774. //
  775. bResult = FALSE;
  776. };
  777. }
  778. }
  779. return bResult;
  780. }
  781. BOOL
  782. FindFreeSpace(
  783. IN PRTL_GENERIC_TABLE pTable,
  784. IN BOOL DeleteUnusedEntries,
  785. IN LONGLONG MaxStartingLcn
  786. )
  787. {
  788. BOOL done = FALSE;
  789. PFREE_SPACE_ENTRY pFreeSpaceEntry;
  790. BOOLEAN bNext = FALSE;
  791. PVOID pRestartKey = NULL;
  792. ULONG ulDeleteCount = 0;
  793. FREE_SPACE_ENTRY entry;
  794. ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
  795. do {
  796. pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
  797. pTable,
  798. NULL,
  799. NULL,
  800. bNext,
  801. &pRestartKey,
  802. &ulDeleteCount,
  803. &entry);
  804. bNext = TRUE;
  805. if (!pFreeSpaceEntry) {
  806. // No free space left
  807. VolData.pFreeSpaceEntry = NULL;
  808. VolData.FoundLcn = 0;
  809. VolData.FoundLen = 0;
  810. break;
  811. }
  812. if (pFreeSpaceEntry->StartingLcn > MaxStartingLcn) {
  813. break;
  814. }
  815. if (pFreeSpaceEntry->ClusterCount < VolData.NumberOfClusters) {
  816. // This space is of no use to us--it's too small.
  817. if ((0 == pFreeSpaceEntry->ClusterCount) || (DeleteUnusedEntries)) {
  818. RtlDeleteElementGenericTable(pTable, pFreeSpaceEntry);
  819. }
  820. }
  821. else {
  822. VolData.pFreeSpaceEntry = pFreeSpaceEntry;
  823. VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
  824. VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
  825. done = TRUE;
  826. }
  827. } while (!done);
  828. return done;
  829. }
  830. BOOL
  831. FindSortedFreeSpace(
  832. IN PRTL_GENERIC_TABLE pTable
  833. )
  834. {
  835. BOOL bFound = FALSE;
  836. PFREE_SPACE_ENTRY pFreeSpaceEntry;
  837. PVOID pRestartKey = NULL;
  838. ULONG ulDeleteCount = 0;
  839. FREE_SPACE_ENTRY entry;
  840. ZeroMemory(&entry, sizeof(FREE_SPACE_ENTRY));
  841. entry.ClusterCount = VolData.NumberOfClusters;
  842. entry.SortBySize = TRUE;
  843. pFreeSpaceEntry = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableLikeADirectory(
  844. pTable,
  845. NULL,
  846. NULL,
  847. FALSE,
  848. &pRestartKey,
  849. &ulDeleteCount,
  850. &entry);
  851. if (!pFreeSpaceEntry) {
  852. // No free space left
  853. VolData.pFreeSpaceEntry = NULL;
  854. VolData.FoundLcn = 0;
  855. VolData.FoundLen = 0;
  856. bFound = FALSE;
  857. }
  858. else {
  859. VolData.pFreeSpaceEntry = pFreeSpaceEntry;
  860. VolData.FoundLcn = pFreeSpaceEntry->StartingLcn;
  861. VolData.FoundLen = pFreeSpaceEntry->ClusterCount;
  862. bFound = TRUE;
  863. }
  864. return bFound;
  865. }
  866. /*****************************************************************************************************************
  867. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  868. ROUTINE DESCRIPTION:
  869. Common routine for most requests to locate free space.
  870. This routine searches the area of the volume delimited by
  871. the variables VolData.DestStartLcn and VolData.DestEndLcn.
  872. Input parm Type specifies which type of free space to find:
  873. FIRST_FIT Find earliest free space extent large enough to hold the entire file
  874. BEST_FIT Find smallest free space extent large enough to hold the entire file
  875. LAST_FIT Find latest free space extent large enough to hold the entire file
  876. EARLIER Locate the start of the free space extent adjacent to the start of this file
  877. FIND_FIRST Find earliest free space extent
  878. FIND_LAST Find latest free space extent
  879. FIND_LARGEST Find largest free space extent
  880. AT_END Find the Lcn to position the data at the end of the found free space extent
  881. COUNT_FREE_SPACES Count the nuumber of free spaces
  882. INPUT + OUTPUT:
  883. IN Type - Which type of free space to find
  884. GLOBALS:
  885. IN OUT Various VolData fields.
  886. RETURN:
  887. TRUE - Success
  888. FALSE - Failure
  889. */
  890. BOOL
  891. FindFreeSpace(
  892. IN int Type
  893. )
  894. {
  895. BOOL bRetStatus = TRUE;
  896. LONGLONG StartingLcn;
  897. LONGLONG EndingLcn;
  898. LONGLONG ClusterCount;
  899. TCHAR cString[500];
  900. LONGLONG Lcn;
  901. PULONG pBitmap = NULL;
  902. LONGLONG BestFitStart = 0;
  903. LONGLONG BestFitLength = 0x7fffffffffffffff;
  904. LONGLONG RunLength = VolData.NumberOfClusters;
  905. LONGLONG LargestFound = 0;
  906. LONGLONG RangeStart = VolData.DestStartLcn;
  907. LONGLONG RangeEnd = VolData.DestEndLcn;
  908. switch(Type) {
  909. case FIRST_FIT:
  910. Message(TEXT("FindFreeSpace - FIRST_FIT"), -1, NULL);
  911. break;
  912. case BEST_FIT:
  913. Message(TEXT("FindFreeSpace - BEST_FIT"), -1, NULL);
  914. break;
  915. case LAST_FIT:
  916. Message(TEXT("FindFreeSpace - LAST_FIT"), -1, NULL);
  917. break;
  918. case EARLIER:
  919. Message(TEXT("FindFreeSpace - EARLIER"), -1, NULL);
  920. break;
  921. case FIND_FIRST:
  922. Message(TEXT("FindFreeSpace - FIND_FIRST"), -1, NULL);
  923. break;
  924. case FIND_LAST:
  925. Message(TEXT("FindFreeSpace - FIND_LAST"), -1, NULL);
  926. break;
  927. case FIND_LARGEST:
  928. Message(TEXT("FindFreeSpace - FIND_LARGEST"), -1, NULL);
  929. break;
  930. case AT_END:
  931. Message(TEXT("FindFreeSpace - AT_END"), -1, NULL);
  932. break;
  933. case COUNT_FREE_SPACES:
  934. Message(TEXT("FindFreeSpace - COUNT_FREE_SPACES"), -1, NULL);
  935. break;
  936. default:
  937. Message(TEXT("FindFreeSpace - ERROR"), -1, NULL);
  938. return FALSE;
  939. }
  940. __try {
  941. //0.0E00 Preset return values for failure
  942. VolData.Status = NEXT_ALGO_STEP;
  943. VolData.FoundLen = 0;
  944. VolData.FreeSpaces = 0;
  945. VolData.LargestFound = 0;
  946. //0.0E00 If find earlier than file is requested set the end of the search range to start of file
  947. if(Type & EARLIER) {
  948. RangeStart = 0;
  949. RangeEnd = VolData.LastStartingLcn;
  950. }
  951. //0.0E00 If find largest is requested preset best found so far to smallest value
  952. if(Type & FIND_LARGEST) {
  953. BestFitLength = 0;
  954. }
  955. #ifndef OFFLINEDK
  956. //0.0E00 If find earliest requested preset size of minimum free space extent to search for.
  957. if(Type & FIND_FIRST) {
  958. RunLength = 1;
  959. }
  960. #endif
  961. //0.0E00 Get a pointer to the free space bitmap
  962. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  963. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
  964. bRetStatus = FALSE;
  965. EH_ASSERT(FALSE);
  966. __leave;
  967. }
  968. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  969. //0.0E00 Scan thru the specified range looking for the specified type of free space extent
  970. for(Lcn = RangeStart; Lcn < RangeEnd; ) {
  971. //0.0E00 Find the next free space extent within the range
  972. FindFreeExtent(pBitmap, RangeEnd, &Lcn, &StartingLcn, &ClusterCount);
  973. //0.0E00 Stop if there are no more free space extents within the range
  974. if(ClusterCount == 0) {
  975. break;
  976. }
  977. //0.0E00 Calc the end of this free space extent
  978. EndingLcn = StartingLcn + ClusterCount;
  979. //0.0E00 Stop if earlier than file requested and this free space later than file
  980. if((Type & EARLIER) && (EndingLcn > VolData.LastStartingLcn)) {
  981. break;
  982. }
  983. //0.0E00 If NTFS and range is outside MFT zone then clip the free space extent to exclude the MFT zone
  984. if((VolData.FileSystem == FS_NTFS) && (VolData.DestEndLcn != VolData.MftZoneEnd)) {
  985. if((StartingLcn < VolData.MftZoneEnd) &&
  986. (EndingLcn > VolData.MftZoneStart)) {
  987. if(StartingLcn < VolData.MftZoneStart) {
  988. EndingLcn = VolData.MftZoneStart;
  989. }
  990. else if(EndingLcn <= VolData.MftZoneEnd) {
  991. continue;
  992. //0.0E00 Handle the case of EndingLcn > pVolData->MftZoneEnd.
  993. }
  994. else {
  995. StartingLcn = VolData.MftZoneEnd;
  996. }
  997. }
  998. }
  999. if(ClusterCount >= LargestFound) {
  1000. LargestFound = ClusterCount;
  1001. }
  1002. //0.0E00 If we are only counting free space just bump the count and skip to the next free space extent
  1003. if(Type & COUNT_FREE_SPACES) {
  1004. VolData.FreeSpaces ++;
  1005. continue;
  1006. }
  1007. //0.0E00 Record this free space extent if any of the following conditions are met:
  1008. //0.0E00 Find earliest or find latest requested
  1009. //0.0E00 Find first fit or find last fit or find earlier and the free space extent large enough
  1010. //0.0E00 Find largest and the free space extent largest so far
  1011. //0.0E00 Find best fit and the free space extent smallest so far that is large enough
  1012. if( (Type & (FIND_FIRST | FIND_LAST)) ||
  1013. ((Type & (LAST_FIT | FIRST_FIT | EARLIER)) && (ClusterCount >= RunLength)) ||
  1014. ((Type & FIND_LARGEST) && (ClusterCount > BestFitLength)) ||
  1015. ((Type & BEST_FIT) && (ClusterCount >= RunLength) && (ClusterCount < BestFitLength)) ) {
  1016. BestFitStart = StartingLcn;
  1017. BestFitLength = ClusterCount;
  1018. }
  1019. //0.0E00 Stop if find first...it has been found
  1020. if(Type & FIND_FIRST) {
  1021. break;
  1022. }
  1023. //0.0E00 If find last fit and large enough skip to next free space extent
  1024. if((Type & LAST_FIT) && (ClusterCount >= RunLength)) {
  1025. continue;
  1026. }
  1027. //0.0E00 Stop if first fit or earlier and large enough
  1028. if( (Type & (FIRST_FIT | EARLIER)) &&
  1029. (ClusterCount >= RunLength) ) {
  1030. break;
  1031. }
  1032. }
  1033. //0.0E00 If count free spaces exit now...that has been done
  1034. if(Type & COUNT_FREE_SPACES) {
  1035. bRetStatus = TRUE;
  1036. __leave;
  1037. }
  1038. VolData.LargestFound = LargestFound;
  1039. //0.0E00 If none were found that match the requirements then exit
  1040. if(BestFitLength == 0x7fffffffffffffff) {
  1041. VolData.FoundLen = 0;
  1042. wsprintf(cString,TEXT("No free space on disk for 0x%lX clusters."), (ULONG)RunLength);
  1043. Message(cString, -1, NULL);
  1044. bRetStatus = FALSE;
  1045. __leave;
  1046. }
  1047. //0.0E00 If at end then calc Lcn to position at end of the free space extent
  1048. if(Type & AT_END) {
  1049. BestFitStart = (BestFitStart + BestFitLength) - RunLength;
  1050. BestFitLength = RunLength;
  1051. }
  1052. //0.0E00 Pass back the free space extent's location and length
  1053. VolData.FoundLcn = BestFitStart;
  1054. VolData.FoundLen = BestFitLength;
  1055. wsprintf(cString, TEXT("Found contiguous free space at LCN 0x%lX"), (ULONG)BestFitStart);
  1056. wsprintf(cString+lstrlen(cString), TEXT(" for Cluster Count of 0x%lX"), (ULONG)BestFitLength);
  1057. Message(cString, -1, NULL);
  1058. bRetStatus = TRUE;
  1059. }
  1060. __finally {
  1061. if (VolData.hVolumeBitmap) {
  1062. GlobalUnlock(VolData.hVolumeBitmap);
  1063. }
  1064. }
  1065. return bRetStatus;
  1066. }
  1067. /*****************************************************************************************************************
  1068. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1069. ROUTINE DESCRIPTION:
  1070. Get the volume bitmap and fill the MFT zone and all the buffer beyond the end of the bitmap with not-free.
  1071. The volume bitmap is stored in the Buffer field of the VOLUME_BITMAP_BUFFER structure.
  1072. INPUT + OUTPUT:
  1073. None.
  1074. GLOBALS:
  1075. IN OUT Various VolData fields.
  1076. RETURN:
  1077. TRUE - Success
  1078. FALSE - Failure
  1079. */
  1080. BOOL
  1081. GetVolumeBitmap(
  1082. )
  1083. {
  1084. STARTING_LCN_INPUT_BUFFER StartingLcnInputBuffer;
  1085. PVOLUME_BITMAP_BUFFER pVolumeBitmap = NULL;
  1086. PULONG pBitmap = NULL;
  1087. ULONG BytesReturned = 0;
  1088. LONGLONG Cluster;
  1089. BOOL bRetStatus = FALSE; // assume an error
  1090. __try {
  1091. //0.0E00 Lock and clear the bitmap buffer
  1092. pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1093. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
  1094. bRetStatus = FALSE;
  1095. EH_ASSERT(FALSE);
  1096. __leave;
  1097. }
  1098. ZeroMemory(pVolumeBitmap, (DWORD)(sizeof(VOLUME_BITMAP_BUFFER) + (VolData.BitmapSize / 8)));
  1099. //0.0E00 Load the bitmap
  1100. StartingLcnInputBuffer.StartingLcn.QuadPart = 0;
  1101. pVolumeBitmap->BitmapSize.QuadPart = VolData.BitmapSize;
  1102. BOOL ret = ESDeviceIoControl(
  1103. VolData.hVolume,
  1104. FSCTL_GET_VOLUME_BITMAP,
  1105. &StartingLcnInputBuffer,
  1106. sizeof(STARTING_LCN_INPUT_BUFFER),
  1107. pVolumeBitmap,
  1108. (DWORD)GlobalSize(VolData.hVolumeBitmap),
  1109. &BytesReturned,
  1110. NULL);
  1111. if (!ret){
  1112. bRetStatus = FALSE;
  1113. EH_ASSERT(FALSE);
  1114. __leave;
  1115. }
  1116. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1117. //0.0E00 Fill the MFT zone with not-free
  1118. /* if(VolData.FileSystem == FS_NTFS) {
  1119. Cluster = VolData.MftZoneStart;
  1120. while((MODULUSLONGLONGBY32(Cluster) != 0) && (Cluster < VolData.MftZoneEnd)) {
  1121. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1122. Cluster ++;
  1123. }
  1124. if(Cluster < VolData.MftZoneEnd) {
  1125. while(VolData.MftZoneEnd - Cluster >= 32) {
  1126. pBitmap[DIVIDELONGLONGBY32(Cluster)] = 0xffffffff;
  1127. Cluster += 32;
  1128. }
  1129. while(Cluster < VolData.MftZoneEnd) {
  1130. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1131. Cluster ++;
  1132. }
  1133. }
  1134. }
  1135. */ //0.0E00 Fill the end of bitmap with not-free
  1136. for(Cluster = VolData.TotalClusters; Cluster < VolData.BitmapSize; Cluster ++) {
  1137. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1138. }
  1139. // #DK312_083 JLJ 20May99
  1140. // Make sure there's a zero bit in the fill-space (to stop the
  1141. // used space/free space optimizations)
  1142. Cluster = VolData.TotalClusters + 1;
  1143. pBitmap[DIVIDELONGLONGBY32(Cluster)] &= ~(1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1144. bRetStatus = TRUE;
  1145. }
  1146. __finally {
  1147. if(VolData.hVolumeBitmap) {
  1148. GlobalUnlock(VolData.hVolumeBitmap);
  1149. }
  1150. }
  1151. return bRetStatus;
  1152. }
  1153. /*****************************************************************************************************************
  1154. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1155. ROUTINE DESCRIPTION:
  1156. Fills the MFT zone with not-free. (This is an ham-handed way of ensuring we
  1157. don't move files into the MFT zone.) The volume bitmap is stored in the Buffer
  1158. field of the VOLUME_BITMAP_BUFFER structure.
  1159. INPUT + OUTPUT:
  1160. None.
  1161. GLOBALS:
  1162. IN OUT Various VolData fields.
  1163. RETURN:
  1164. TRUE - Success
  1165. FALSE - Failure
  1166. */
  1167. BOOL
  1168. MarkMFTUnavailable(void)
  1169. {
  1170. //0.0E00 Lock the bitmap buffer and get a pointer to it...
  1171. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1172. EF(pVolumeBitmap != NULL);
  1173. //0.0E00 Get a pointer to the actual bitmap portion
  1174. PULONG pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1175. //0.0E00 Mark the MFT zone as "not free"
  1176. if(VolData.FileSystem == FS_NTFS) {
  1177. LONGLONG Cluster = VolData.MftZoneStart;
  1178. // this loop marks the bits of the first DWORD in the bitmap
  1179. // for the first few clusters of the MFT zone
  1180. while (MODULUSLONGLONGBY32(Cluster) != 0 && Cluster < VolData.MftZoneEnd) {
  1181. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1182. Cluster++;
  1183. }
  1184. // this section gets the rest of the clusters
  1185. if(Cluster < VolData.MftZoneEnd) {
  1186. // mark out the groups of 32 cluster sections
  1187. while(VolData.MftZoneEnd - Cluster >= 32) {
  1188. pBitmap[DIVIDELONGLONGBY32(Cluster)] = 0xffffffff;
  1189. Cluster += 32;
  1190. }
  1191. // mark out the remaining bits of the last section of the MFT zone
  1192. while(Cluster < VolData.MftZoneEnd) {
  1193. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  1194. Cluster++;
  1195. }
  1196. }
  1197. }
  1198. //0.0E00 Finally, unlock the bitmap...
  1199. if(VolData.hVolumeBitmap) {
  1200. GlobalUnlock(VolData.hVolumeBitmap);
  1201. }
  1202. return TRUE;
  1203. }
  1204. /*****************************************************************************************************************
  1205. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1206. ROUTINE DESCRIPTION:
  1207. Check the clusters adjacent to the start of this file to see if they are free.
  1208. INPUT + OUTPUT:
  1209. None.
  1210. GLOBALS:
  1211. IN OUT Various VolData fields.
  1212. RETURN:
  1213. TRUE - Success
  1214. FALSE - Failure
  1215. */
  1216. BOOL
  1217. IsFreeSpaceAtHeadOfFile(
  1218. )
  1219. {
  1220. BOOL bRetStatus = FALSE;
  1221. PVOLUME_BITMAP_BUFFER pVolumeBitmap = NULL;
  1222. PULONG pBitmap = NULL;
  1223. ULONG Mask = 1;
  1224. LONGLONG Word = 0;
  1225. LONGLONG Bit = 0;
  1226. LONGLONG StartingLcn = VolData.StartingLcn;
  1227. LONGLONG EndingLcn = VolData.StartingLcn;
  1228. LONGLONG ClusterCount;
  1229. TCHAR cString[500];
  1230. __try {
  1231. //0.0E00 Preset status in case an error occurs
  1232. VolData.Status = NEXT_ALGO_STEP;
  1233. VolData.FoundLen = 0;
  1234. //0.0E00 Lock the bitmap and get a pointer to it
  1235. pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1236. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
  1237. bRetStatus = FALSE;
  1238. EH_ASSERT(FALSE);
  1239. __leave;
  1240. }
  1241. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1242. //0.0E00 Setup bitmap indexes
  1243. Word = DIVIDELONGLONGBY32(StartingLcn - 1);
  1244. Mask = 1 << (((ULONG)StartingLcn - 1) & 31);
  1245. //0.0E00 Check for file against start of disk or one of the MFT zones
  1246. if((StartingLcn == 0) ||
  1247. (StartingLcn == VolData.MftZoneEnd) ||
  1248. (StartingLcn == VolData.CenterOfDisk) ||
  1249. ((pBitmap[Word] & Mask) != 0)) {
  1250. Message(TEXT("No free space before this file"), -1, NULL);
  1251. bRetStatus = TRUE;
  1252. __leave;
  1253. }
  1254. //0.0E00 On FAT volumes any space before the file is enough
  1255. if(VolData.FileSystem != FS_NTFS) {
  1256. VolData.FoundLen = 1;
  1257. Message(TEXT("Found free space before this file"), -1, NULL);
  1258. bRetStatus = TRUE;
  1259. __leave;
  1260. }
  1261. //0.0E00 Find the start of the free space preceding this file
  1262. while((Word >= 0) && ((pBitmap[Word] & Mask) == 0)) {
  1263. Mask >>= 1;
  1264. if(Mask == 0) {
  1265. Mask = 0x80000000;
  1266. for (Word--; (Word >= 0) && (pBitmap[Word] == 0); Word--)
  1267. ;
  1268. }
  1269. }
  1270. Mask <<= 1;
  1271. if(Mask == 0) {
  1272. Mask = 1;
  1273. Word ++;
  1274. }
  1275. for(Bit = -1; Mask; Mask >>= 1, Bit ++);
  1276. //0.0E00 Convert the indexes into an Lcn
  1277. StartingLcn = (Word * 32) + Bit;
  1278. //0.0E00 Adjust for free space that spans the center of the disk
  1279. if((VolData.StartingLcn >= VolData.CenterOfDisk) && (StartingLcn < VolData.CenterOfDisk)) {
  1280. StartingLcn = VolData.CenterOfDisk;
  1281. }
  1282. //0.0E00 Adjust for free space that spans the MFT zone
  1283. if(VolData.FileSystem == FS_NTFS) {
  1284. if((StartingLcn < VolData.MftZoneEnd) &&
  1285. (EndingLcn > VolData.MftZoneStart)) {
  1286. if(StartingLcn < VolData.MftZoneStart) {
  1287. EndingLcn = VolData.MftZoneStart;
  1288. }
  1289. else if(EndingLcn <= VolData.MftZoneEnd) {
  1290. StartingLcn = VolData.MftZoneEnd;
  1291. EndingLcn = VolData.MftZoneEnd;
  1292. }
  1293. //0.0E00 Handle the case of EndingLcn > VolData.MftZoneEnd.
  1294. else {
  1295. StartingLcn = VolData.MftZoneEnd;
  1296. }
  1297. }
  1298. }
  1299. ClusterCount = EndingLcn - StartingLcn;
  1300. //0.0E00 Return the location and size of the free space found
  1301. VolData.FoundLcn = StartingLcn;
  1302. VolData.FoundLen = ClusterCount;
  1303. wsprintf(cString, TEXT("Free space before file at LCN 0x%X"), VolData.FoundLcn);
  1304. wsprintf(cString+lstrlen(cString), TEXT(" for Cluster Count of 0x%lX\n"), VolData.FoundLen);
  1305. Message(cString, -1, NULL);
  1306. bRetStatus = TRUE;
  1307. }
  1308. __finally {
  1309. if (VolData.hVolumeBitmap) {
  1310. GlobalUnlock(VolData.hVolumeBitmap);
  1311. }
  1312. }
  1313. return bRetStatus;
  1314. }
  1315. /*********************************************************************************************
  1316. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1317. ROUTINE DESCRIPTION:
  1318. Find the next run of un-allocated clusters.
  1319. Bits set to '1' in the volume bitmap are allocated or "used".
  1320. Bits set to '0' in the volume bitmap are un-allocated or "free".
  1321. A bit set to '1' is considered "set".
  1322. A bit set to '0' is considered "clear".
  1323. INPUT + OUTPUT:
  1324. IN pClusterBitmap - Volume cluster bitmap
  1325. IN RangeEnd - End of the area of the volume to search in
  1326. IN OUT pLcn - Where to start searching and returns where search stopped
  1327. OUT pStartingLcn - Returns where the free run starts
  1328. OUT pClusterCount - Returns the length of the free run
  1329. GLOBALS:
  1330. None.
  1331. RETURN:
  1332. None
  1333. */
  1334. VOID
  1335. FindFreeExtent(
  1336. IN PULONG pClusterBitmap,
  1337. IN LONGLONG RangeEnd,
  1338. IN OUT PLONGLONG pLcn,
  1339. OUT PLONGLONG pStartingLcn,
  1340. OUT PLONGLONG pClusterCount
  1341. )
  1342. {
  1343. // default returns to 0
  1344. *pStartingLcn = 0;
  1345. *pClusterCount = 0;
  1346. // check for bad LCN input
  1347. if (pLcn == NULL) {
  1348. Message(TEXT("FindFreeExtent called with NULL arg"), -1, NULL);
  1349. assert(pLcn != NULL);
  1350. return;
  1351. }
  1352. // check for bad RangeEnd
  1353. if (RangeEnd > VolData.TotalClusters) {
  1354. Message(TEXT("FindFreeExtent called with RangeEnd > TotalClusters"), -1, NULL);
  1355. assert(RangeEnd <= VolData.TotalClusters);
  1356. return;
  1357. }
  1358. // if requester asks for extent past end of volume, don't honor it.
  1359. if (*pLcn >= RangeEnd) {
  1360. Message(TEXT("FindFreeExtent called with LCN > RangeEnd"), -1, NULL);
  1361. return;
  1362. }
  1363. // iBitmapWord - index into the cluster bitmap array (array of ULONGs)
  1364. // divide lcn by word size (word index) = Lcn / 32 (same as shift right 5)
  1365. ULONG iBitmapWord = (ULONG) DIVIDELONGLONGBY32(*pLcn);
  1366. // iBit - index of the bit in the current word
  1367. // remainder (bit index in current word) = Lcn % 32 (same as and'ing lowest 5 bits)
  1368. ULONG iBit = (ULONG) MODULUSLONGLONGBY32(*pLcn);
  1369. // LCN should always be the cluster array index times 32 plus the bit index
  1370. assert(*pLcn == (iBitmapWord * 32) + iBit);
  1371. // an empty word except for the starting Lcn bit
  1372. ULONG Mask = (ULONG) (1 << iBit);
  1373. // find the start of the next free space extent
  1374. while (*pLcn < RangeEnd && *pStartingLcn == 0) {
  1375. // checks
  1376. assert(*pLcn == (iBitmapWord * 32) + iBit);
  1377. assert(Mask != 0);
  1378. assert(iBit >= 0 && iBit < 32);
  1379. // if there are no free bits in the entire word we can jump ahead
  1380. // we can only check this on a word boundary when iBit = 0
  1381. if (iBit == 0 && pClusterBitmap[iBitmapWord] == 0xFFFFFFFF) {
  1382. (*pLcn) += 32;
  1383. iBitmapWord++;
  1384. Mask = 1;
  1385. continue;
  1386. }
  1387. // otherwise, check for a free cluster: bit = 0
  1388. else if (!(pClusterBitmap[iBitmapWord] & Mask)) {
  1389. *pStartingLcn = *pLcn; // mark start of free space extent
  1390. }
  1391. // go to the next cluster
  1392. (*pLcn)++;
  1393. iBit++;
  1394. Mask <<= 1;
  1395. // check for crossing into the next word of the cluster array
  1396. if (iBit >= 32) {
  1397. iBit = 0;
  1398. iBitmapWord++;
  1399. Mask = 1;
  1400. }
  1401. }
  1402. // LCN should always be the cluster array index times 32 plus the bit index
  1403. assert(*pLcn == (iBitmapWord * 32) + iBit);
  1404. // if we found a free cluster
  1405. if (*pStartingLcn > 0) {
  1406. // find the next used cluster
  1407. // and count the free clusters
  1408. while (*pLcn < RangeEnd) {
  1409. // checks
  1410. assert(*pLcn == (iBitmapWord * 32) + iBit);
  1411. assert(Mask != 0);
  1412. assert(iBit >= 0 && iBit < 32);
  1413. // if the entire word is free space we can jump ahead
  1414. // we can only check this on a word boundary when iBit = 0
  1415. if (iBit == 0 && pClusterBitmap[iBitmapWord] == 0) {
  1416. (*pLcn) += 32;
  1417. iBitmapWord++;
  1418. Mask = 1;
  1419. continue;
  1420. }
  1421. // otherwise, check for a used cluster: bit = 1
  1422. else if (pClusterBitmap[iBitmapWord] & Mask) {
  1423. break; // we're done
  1424. }
  1425. // go to the next cluster
  1426. (*pLcn)++;
  1427. iBit++;
  1428. Mask <<= 1;
  1429. // check for crossing into the next word of the cluster array
  1430. if (iBit >= 32) {
  1431. iBit = 0;
  1432. iBitmapWord++;
  1433. Mask = 1;
  1434. }
  1435. }
  1436. }
  1437. // set cluster count
  1438. if (*pStartingLcn > 0) {
  1439. *pClusterCount = *pLcn - *pStartingLcn;
  1440. }
  1441. // sanity check of outputs
  1442. assert(*pClusterCount == 0 && *pStartingLcn == 0 ||
  1443. *pClusterCount > 0 && *pStartingLcn > 0);
  1444. }
  1445. /*****************************************************************************************************************
  1446. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1447. ROUTINE DESCRIPTION:
  1448. This routine is part of the so-called "partial defrag" code. So called because
  1449. in its current incarnation it really works on the whole file, not part of the file.
  1450. This routine is called because there was no contiguous run of free space on the
  1451. volume long enough to hold the entire file. This routine uses the current size
  1452. of the file to estimate how many freespace extents might be needed to hold the
  1453. file (different if FAT or NTFS) and uses that data to fill an allocated array
  1454. with free space extents. This array is then used in the Partial Defrag routine
  1455. to move the file around.
  1456. The purpose of this routine is find free space that will get the file out of the
  1457. way so that the rest of the volume can be defragmented. It is not always clear
  1458. how one should accomplish this, however, so the routine gathers the data in different
  1459. ways: toward the front of the partition, toward the back of the partition, or just
  1460. using the largest available free extents.
  1461. INPUT + OUTPUT:
  1462. None.
  1463. GLOBALS:
  1464. IN OUT Various VolData fields.
  1465. Allocated and initialized in this routine:
  1466. pVolData->hFreeExtents = free space extent list,
  1467. pVolData->FreeExtents = number of free space extents.
  1468. Updated in this routine:
  1469. VolData.Pass = NEXT_FILE if return=false
  1470. VolData.Pass = NEXT_ALGO_STEP if return=true
  1471. RETURN:
  1472. TRUE - Success
  1473. FALSE - Failure
  1474. */
  1475. BOOL
  1476. FindLastFreeSpaceChunks(
  1477. )
  1478. {
  1479. //the next item is static so it can be used to ping-pong the
  1480. //selection of the sort algorithm. Just didn't seem worth
  1481. //it to add it to Voldata right now.
  1482. BOOL bRetStatus = FALSE;
  1483. LONGLONG FirstLcn = VolData.StartingLcn;
  1484. LONGLONG LastLcn = VolData.DestEndLcn;
  1485. LONGLONG RunLength = VolData.NumberOfClusters;
  1486. EXTENT_LIST* pExtentList = NULL;
  1487. LONGLONG FreeExtent = 0;
  1488. TCHAR cString[500];
  1489. LONGLONG SearchStartLcn = 0;
  1490. LONGLONG FreeStartLcn;
  1491. LONGLONG FreeCount;
  1492. LONG iii,jjj;
  1493. Message(TEXT("FindLastFreeSpaceChunks"), -1, NULL);
  1494. //0.0E00 Preset clusters found to none in case not enough are found
  1495. VolData.Status = NEXT_ALGO_STEP;
  1496. VolData.FoundLen = 0;
  1497. __try {
  1498. //0.0E00 Lock the volume bitmap, and get a pointer to it
  1499. VOLUME_BITMAP_BUFFER *pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1500. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
  1501. EH_ASSERT(FALSE);
  1502. __leave;
  1503. }
  1504. //get a pointer that points to the bitmap part of the bitmap
  1505. //(past the header)
  1506. ULONG *pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1507. //estimate the extent list entries needed to hold the file's new extent list
  1508. //(Note, this is purely speculative jlj 15Apr99)
  1509. if(VolData.FileSystem == FS_NTFS) {
  1510. VolData.FreeExtents = 2 * (VolData.NumberOfClusters / 16);
  1511. }
  1512. else {
  1513. VolData.FreeExtents = VolData.NumberOfClusters;
  1514. }
  1515. // limit the estimate to 2000 extents (at least for now)
  1516. //(Note, this is purely speculative jlj 15Apr99)
  1517. if (VolData.FreeExtents > 2000) {
  1518. VolData.FreeExtents = 2000;
  1519. }
  1520. //John Joesph change not approved yet
  1521. //VolData.FreeExtents = VolData.currentnumberoffragments -1.
  1522. //if VolData.FreeExtents == 0 do something
  1523. //0.0E00 Allocate a buffer for the free space extent list
  1524. if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
  1525. VolData.Status = NEXT_FILE;
  1526. EH_ASSERT(FALSE);
  1527. __leave;
  1528. }
  1529. // This section of code scans the free space bitmap and finds the largest
  1530. // chunks, but no more than VolData.FreeExtents chunks
  1531. // This code basically loops through the free space, gathering
  1532. // the statistics for each fragment of free space, starting with
  1533. // cluster zero
  1534. SearchStartLcn = 0;
  1535. while (SearchStartLcn != -1) {
  1536. // go get a the next chunk of free space
  1537. FindFreeExtent((PULONG) pBitmap, // pointer to the volume bitmap gotten from locking
  1538. VolData.TotalClusters, // end of range
  1539. &SearchStartLcn, // where to start
  1540. &FreeStartLcn, // where the free block starts
  1541. &FreeCount); // how big the block is (zero if done)
  1542. //a return value of zero means the end of the partition has
  1543. //been hit, so if we hit the end of the partition, stop the
  1544. //loop.
  1545. if (FreeCount == 0) {
  1546. SearchStartLcn = -1;
  1547. }
  1548. else {
  1549. //if there's space in the array, just insert it into the array
  1550. if (FreeExtent< (VolData.FreeExtents-1)) {
  1551. //0.0E00 Add this extent to the free space extent list
  1552. pExtentList[FreeExtent].StartingLcn = FreeStartLcn;
  1553. pExtentList[FreeExtent].ClusterCount = FreeCount;
  1554. FreeExtent++;
  1555. continue;
  1556. }
  1557. // replace the current min with a bigger min
  1558. UINT uMinIndex = 0;
  1559. LONGLONG llMinClusterCount = 99999;
  1560. for (iii=0; iii<FreeExtent; iii++) {
  1561. if (pExtentList[iii].ClusterCount < llMinClusterCount) {
  1562. uMinIndex = iii;
  1563. llMinClusterCount = pExtentList[iii].ClusterCount;
  1564. }
  1565. }
  1566. if (FreeCount > llMinClusterCount){
  1567. pExtentList[iii].StartingLcn = FreeStartLcn;
  1568. pExtentList[iii].ClusterCount = FreeCount;
  1569. }
  1570. } //end else (i.e. FoundCount != 0)
  1571. } // end while searchstartlcn != -1
  1572. // add up the clusters found
  1573. LONGLONG FoundClusterCount = 0;
  1574. for (iii=0; iii<FreeExtent; iii++) {
  1575. FoundClusterCount = FoundClusterCount + pExtentList[iii].ClusterCount;
  1576. }
  1577. // not enough room to hold the file
  1578. if (FoundClusterCount < VolData.NumberOfClusters) {
  1579. Message(TEXT("FLFSC:Couldn't find enough free space for this file"), -1, NULL);
  1580. VolData.Status = NEXT_FILE;
  1581. __leave;
  1582. }
  1583. if (FreeExtent <= 1) {
  1584. Message(TEXT("FLFSC: NO FREE SPACE CHUNKS FOUND ON THIS VOLUME!!!"), -1, NULL);
  1585. VolData.Status = NEXT_FILE;
  1586. __leave;
  1587. }
  1588. //sort from big to small
  1589. EXTENT_LIST tmpExtentList;
  1590. BOOL bSwitched = TRUE;
  1591. for (iii=0; iii<(FreeExtent-1) && bSwitched; iii++) {
  1592. bSwitched = FALSE;
  1593. for (jjj=0; jjj<(FreeExtent-1); jjj++) {
  1594. if (pExtentList[jjj].ClusterCount < pExtentList[jjj+1].ClusterCount) {
  1595. tmpExtentList = pExtentList[jjj];
  1596. pExtentList[jjj] = pExtentList[jjj+1];
  1597. pExtentList[jjj+1] = tmpExtentList;
  1598. bSwitched = TRUE;
  1599. }
  1600. }
  1601. }
  1602. //0.0E00 If we found extents larger than the minimum we may have less extents than expected
  1603. VolData.FreeExtents = FreeExtent-1;
  1604. //0.0E00 Reallocate the free space entent list
  1605. if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
  1606. bRetStatus = FALSE;
  1607. EH_ASSERT(FALSE);
  1608. __leave;
  1609. }
  1610. wsprintf(cString,TEXT("FLFSC: Found 0x%lX free extents for 0x%lX Clusters"),
  1611. (ULONG)VolData.FreeExtents, (ULONG)FoundClusterCount);
  1612. Message(cString, -1, NULL);
  1613. //0.0E00 Flag that we were successful
  1614. VolData.FoundLcn = pExtentList->StartingLcn;
  1615. VolData.FoundLen = FoundClusterCount;
  1616. bRetStatus = TRUE;
  1617. }
  1618. __finally {
  1619. if (VolData.hVolumeBitmap) {
  1620. GlobalUnlock(VolData.hVolumeBitmap);
  1621. }
  1622. if (VolData.hFreeExtents) {
  1623. GlobalUnlock(VolData.hFreeExtents);
  1624. }
  1625. }
  1626. return bRetStatus;
  1627. }
  1628. /*****************************************************************************************************************
  1629. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1630. REVISION HISTORY:
  1631. 0.0E00 - 26 February 1996 - Chuck Tingley - created
  1632. 1.0E00 - 24 April 1997 - Zack Gainsforth - Reworked module for Dfrg engine.
  1633. ROUTINE DESCRIPTION:
  1634. Partial defrag code.
  1635. There is no contiguous run of free space on the volume long enough to hold the entire file.
  1636. So we gonna move the file anyaway and in such a way that we hope will increase our chances
  1637. to defragment the drive. We gonna take the square root of the number of clusters in the file.
  1638. Then we look for that many runs (or less) of that many free clusters per run (or more).
  1639. This routine finds this set of free cluster runs and builds an extent list to describe the set.
  1640. Then Another routine is called to move the file into these free space extents.
  1641. Since the purpose here is to get this file out of the way so the rest of the volume can be
  1642. defragmented, the search is done from the end of the volume backward.
  1643. pVolData->hFreeExtents = free space extent list, pVolData->FreeExtents = number of free space extents.
  1644. INPUT + OUTPUT:
  1645. None.
  1646. GLOBALS:
  1647. IN OUT Various VolData fields.
  1648. RETURN:
  1649. TRUE - Success
  1650. FALSE - Failure
  1651. */
  1652. /*
  1653. BOOL
  1654. FindLastFreeSpaceChunks(
  1655. )
  1656. {
  1657. BOOL bRetStatus = TRUE;
  1658. VOLUME_BITMAP_BUFFER* pVolumeBitmap = NULL;
  1659. ULONG* pBitmap = NULL;
  1660. LONGLONG FirstLcn = VolData.StartingLcn;
  1661. LONGLONG LastLcn = VolData.DestEndLcn;
  1662. LONGLONG Lcn;
  1663. LONGLONG StartingLcn;
  1664. LONGLONG ClusterCount;
  1665. LONGLONG RunLength = VolData.NumberOfClusters;
  1666. EXTENT_LIST* pExtentList = NULL;
  1667. LONGLONG ExtentLength;
  1668. LONGLONG FreeExtent = 0;
  1669. LONGLONG Extent;
  1670. LONGLONG LastStartingLcn;
  1671. LONGLONG LastClusterCount;
  1672. LONGLONG FoundClusterCount = 0;
  1673. TCHAR cString[500];
  1674. Message(TEXT("FindLastFreeSpaceChunks"), -1, NULL);
  1675. //0.0E00 Preset clusters found to none in case not enough are found
  1676. VolData.Status = NEXT_ALGO_STEP;
  1677. VolData.FoundLen = 0;
  1678. __try {
  1679. #ifndef OFFLINEDK
  1680. //0.0E00 Round file's total cluster size up to the nearest 16.
  1681. if((VolData.FileSystem == FS_NTFS) && ((RunLength & 0x0F) != 0)) {
  1682. RunLength = (RunLength & 0xFFFFFFFFFFFFFFF0) + 0x10;
  1683. }
  1684. #endif
  1685. //0.0E00 Use the square root of the file's cluster count for number of extents and size of each extent
  1686. ExtentLength = (LONGLONG)sqrt((double)RunLength);
  1687. #ifndef OFFLINEDK
  1688. //0.0E00 Round cluster length of extents up to the nearest 16.
  1689. if((VolData.FileSystem == FS_NTFS) && ((ExtentLength & 0x0F) != 0)) {
  1690. ExtentLength = (ExtentLength & 0xFFFFFFFFFFFFFFF0) + 0x10;
  1691. }
  1692. #endif
  1693. //0.0E00 Number of extents = total clusters / minimum extent length
  1694. if (ExtentLength == 0){
  1695. bRetStatus = FALSE;
  1696. EH_ASSERT(FALSE);
  1697. __leave;
  1698. }
  1699. VolData.FreeExtents = (VolData.NumberOfClusters / ExtentLength) + ((VolData.NumberOfClusters % ExtentLength) ? 1 : 0);
  1700. //0.0E00 Allocate a buffer for the free space extent list
  1701. if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
  1702. bRetStatus = FALSE;
  1703. EH_ASSERT(FALSE);
  1704. __leave;
  1705. }
  1706. //0.0E00 Lock the volume bitmap
  1707. pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1708. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL){
  1709. bRetStatus = FALSE;
  1710. EH_ASSERT(FALSE);
  1711. __leave;
  1712. }
  1713. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1714. //0.0E00 Find the set of the last free space extents of minimum size to hold the file
  1715. for(FoundClusterCount = 0, FreeExtent = 0;
  1716. FoundClusterCount < RunLength;
  1717. FoundClusterCount += LastClusterCount, FreeExtent ++) {
  1718. //0.0E00 Find the last extent that is long enough and not in the list yet
  1719. for(LastClusterCount = 0, Lcn = FirstLcn; Lcn < LastLcn; ) {
  1720. //0.0E00 Find the next extent
  1721. FindFreeExtent(pBitmap, LastLcn, &Lcn, &StartingLcn, &ClusterCount);
  1722. if(ClusterCount == 0) {
  1723. break;
  1724. }
  1725. //0.0E00 If this extent is long enough...
  1726. if(ClusterCount >= ExtentLength) {
  1727. //0.0E00 ...And not already in the list...
  1728. for(Extent = 0;
  1729. (Extent < FreeExtent) &&
  1730. (pExtentList[Extent].StartingLcn != StartingLcn);
  1731. Extent ++)
  1732. ;
  1733. //0.0E00 ...Remember it
  1734. if(Extent >= FreeExtent) {
  1735. LastStartingLcn = StartingLcn;
  1736. LastClusterCount = ClusterCount;
  1737. }
  1738. }
  1739. }
  1740. if(LastClusterCount == 0) {
  1741. break;
  1742. }
  1743. #ifndef OFFLINEDK
  1744. //0.0E00 Round this extent's length down to the nearest 16.
  1745. if(VolData.FileSystem == FS_NTFS) {
  1746. LastClusterCount = LastClusterCount & 0xFFFFFFFFFFFFFFF0;
  1747. }
  1748. #endif
  1749. //0.0E00 Add this extent to the free space extent list
  1750. pExtentList[FreeExtent].StartingLcn = LastStartingLcn;
  1751. pExtentList[FreeExtent].ClusterCount = LastClusterCount;
  1752. }
  1753. //0.0E00 If we found extents larger than the minimum we may have less extents than expected
  1754. if(FreeExtent < VolData.FreeExtents) {
  1755. VolData.FreeExtents = FreeExtent;
  1756. if(VolData.FreeExtents == 0){
  1757. wsprintf(cString,TEXT("0x%lX free extents found for 0x%lX Clusters"), (ULONG)VolData.FreeExtents, (ULONG)RunLength);
  1758. Message(cString, -1, NULL);
  1759. VolData.Status = NEXT_FILE;
  1760. return FALSE;
  1761. }
  1762. //0.0E00 Reallocate the free space entent list
  1763. if (!AllocateMemory((DWORD)(VolData.FreeExtents * sizeof(EXTENT_LIST)), &VolData.hFreeExtents, (void**)&pExtentList)){
  1764. bRetStatus = FALSE;
  1765. EH_ASSERT(FALSE);
  1766. __leave;
  1767. }
  1768. }
  1769. wsprintf(cString,TEXT("Found 0x%lX free extents for 0x%lX Clusters"), (ULONG)VolData.FreeExtents, (ULONG)RunLength);
  1770. Message(cString, -1, NULL);
  1771. //0.0E00 Flag that we were successful
  1772. VolData.FoundLcn = pExtentList->StartingLcn;
  1773. VolData.FoundLen = FoundClusterCount;
  1774. bRetStatus = TRUE;
  1775. }
  1776. __finally {
  1777. if (VolData.hVolumeBitmap) {
  1778. GlobalUnlock(VolData.hVolumeBitmap);
  1779. }
  1780. if (VolData.hFreeExtents) {
  1781. GlobalUnlock(VolData.hFreeExtents);
  1782. }
  1783. }
  1784. return bRetStatus;
  1785. }
  1786. */
  1787. /*****************************************************************************************************************
  1788. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1789. ROUTINE DESCRIPTION:
  1790. Counts the number of free spaces in the bitmap, and calculates stats about the free spaces.
  1791. INPUT + OUTPUT:
  1792. GLOBALS:
  1793. LONGLONG NumFreeSpaces; //The number of free spaces on the volume.
  1794. LONGLONG SmallestFreeSpaceClusters; //The size of the smallest free space in clusters.
  1795. LONGLONG LargestFreeSpaceClusters; //The size of the largest free space in clusters.
  1796. LONGLONG AveFreeSpaceClusters; //The average size of the free spaces in clusters.
  1797. RETURN:
  1798. TRUE - Success
  1799. FALSE - Failure
  1800. */
  1801. BOOL
  1802. CountFreeSpaces(
  1803. )
  1804. {
  1805. PULONG pBitmap;
  1806. LONGLONG LastLcn = 0;
  1807. LONGLONG Lcn = 0;
  1808. LONGLONG StartingLcn = 0;
  1809. LONGLONG ClusterCount = 0;
  1810. VOLUME_BITMAP_BUFFER* pVolumeBitmap = NULL;
  1811. //Initialize the VolData fields.
  1812. VolData.NumFreeSpaces = 0;
  1813. VolData.SmallestFreeSpaceClusters = 0x7FFFFFFFFFFFFFFF;
  1814. VolData.LargestFreeSpaceClusters = 0;
  1815. VolData.AveFreeSpaceClusters = 0;
  1816. //0.0E00 Lock the volume bitmap
  1817. pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1818. EF_ASSERT(pVolumeBitmap);
  1819. //Get a pointer to the bitmap.
  1820. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  1821. //Get the length of the bitmap.
  1822. LastLcn = VolData.TotalClusters;
  1823. //This loop will cycle until the end of the volume.
  1824. do{
  1825. //Find the next extent
  1826. FindFreeExtent(pBitmap, LastLcn, &Lcn, &StartingLcn, &ClusterCount);
  1827. //Check to make sure a free space was found.
  1828. if(ClusterCount == 0) {
  1829. break;
  1830. }
  1831. //Note the running total of free space extents.
  1832. VolData.NumFreeSpaces++;
  1833. //Keep track of the smallest free space extent.
  1834. if(VolData.SmallestFreeSpaceClusters > ClusterCount){
  1835. VolData.SmallestFreeSpaceClusters = ClusterCount;
  1836. }
  1837. //Keep track of the largest free space extent.
  1838. if(VolData.LargestFreeSpaceClusters < ClusterCount){
  1839. VolData.LargestFreeSpaceClusters = ClusterCount;
  1840. }
  1841. //Reset StartingLcn to point to the next position to check from.
  1842. StartingLcn += ClusterCount;
  1843. //Reset ClusterCount.
  1844. ClusterCount = 0;
  1845. }while(StartingLcn < VolData.TotalClusters);
  1846. //Calculate the average free space extent size.
  1847. if(VolData.NumFreeSpaces){
  1848. VolData.AveFreeSpaceClusters = (VolData.TotalClusters - VolData.UsedClusters) / VolData.NumFreeSpaces;
  1849. }
  1850. else{
  1851. VolData.AveFreeSpaceClusters = 0;
  1852. }
  1853. GlobalUnlock(VolData.hVolumeBitmap);
  1854. return TRUE;
  1855. }
  1856. /*****************************************************************************************************************
  1857. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1858. ROUTINE DESCRIPTION:
  1859. Examines the freespace map of the volume and does statistical breakdown on it for
  1860. debugging purposes.
  1861. 29Apr99 - Also summarizes "truly usable freespace" so that user can be informed if
  1862. volume is getting too full for defragmentation.
  1863. OUTPUT:
  1864. LONGLONG * llUsableFreeClusters - clusters that are actually usable
  1865. RETURN:
  1866. TRUE - Success
  1867. FALSE - Failure
  1868. */
  1869. BOOL
  1870. DetermineUsableFreespace(
  1871. LONGLONG *llUsableFreeClusters
  1872. )
  1873. {
  1874. const MAX_BUCKET = 16;
  1875. struct {
  1876. UINT uLowerBound;
  1877. UINT uUpperBound;
  1878. LONGLONG llFragCount;
  1879. LONGLONG llTotalFragSize;
  1880. } freeBuckets[MAX_BUCKET] = {0};
  1881. LONGLONG llPercentOfFreeFragments;
  1882. LONGLONG llPercentOfFreeSpace;
  1883. LONGLONG llTotalFreeClusters = 0;
  1884. LONGLONG llTotalFreeFragments = 0;
  1885. LONGLONG SearchStartLcn = 0;
  1886. LONGLONG FreeStartLcn;
  1887. LONGLONG FreeCount;
  1888. TCHAR cOutline[1024];
  1889. TCHAR cTemp[20];
  1890. UINT iBucket;
  1891. LONGLONG llSumTotalFreeClusters = 0;
  1892. LONGLONG llSumUsableFreeClusters = 0;
  1893. LONGLONG llSumUnusableFreeClusters = 0;
  1894. Trace(log, " ");
  1895. Trace(log, "Start: Determine UsableFreeSpace");
  1896. //clear return
  1897. *llUsableFreeClusters = 0;
  1898. //1.0E00 Load the volume bitmap. (MFT-excluded)
  1899. EF(MarkMFTUnavailable());
  1900. //0.0E00 Lock and grab a pointer to the volume bitmap
  1901. PVOLUME_BITMAP_BUFFER pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(VolData.hVolumeBitmap);
  1902. EF(pVolumeBitmap);
  1903. //set up the first bucket's bounds
  1904. freeBuckets[0].uLowerBound = 0;
  1905. freeBuckets[0].uUpperBound = 2;
  1906. //then fill in the parameters for the remainder of the buckets
  1907. for (iBucket=1; iBucket<MAX_BUCKET; iBucket++) {
  1908. //ensure the low bound of the bucket is the last bucket's high side plus one
  1909. freeBuckets[iBucket].uLowerBound = freeBuckets[iBucket-1].uUpperBound + 1;
  1910. //make the high bound of the bucket 2x the last bucket's high bound
  1911. freeBuckets[iBucket].uUpperBound = freeBuckets[iBucket-1].uUpperBound * 2;
  1912. }
  1913. //last bucket dump headers
  1914. Trace(log, " last bucket detail:");
  1915. Trace(log, " LCN LEN");
  1916. Trace(log, " -------- --------");
  1917. //this is just a copy of the loop that "prints the freespace clusters"
  1918. //above. Basically, it just loops through the free space, gathering
  1919. //the statistics for each fragment of free space, starting with cluster zero
  1920. SearchStartLcn = 0;
  1921. while (SearchStartLcn != -1) {
  1922. // go get a the next chunk of free space
  1923. FindFreeExtent((PULONG) pVolumeBitmap->Buffer, // pointer to the volume bitmap gotten from locking
  1924. VolData.TotalClusters, // end of range
  1925. &SearchStartLcn, // where to start
  1926. &FreeStartLcn, // where the free block starts
  1927. &FreeCount); // how big the block is (zero if done)
  1928. llSumTotalFreeClusters += FreeCount;
  1929. llSumUsableFreeClusters += FreeCount;
  1930. //a return value of zero means the end of the bitmap has
  1931. //been hit, so if we hit the end of the bitmap, stop the
  1932. //loop.
  1933. if (FreeCount == 0) {
  1934. SearchStartLcn = -1;
  1935. }
  1936. else { //we got a value, so do the statistics on it.
  1937. *llUsableFreeClusters += FreeCount;
  1938. //see if the free space fragment fits in a bucket
  1939. //(I guess this could have been done with a binary search
  1940. //method, but why bother for 16 items?)
  1941. BOOL bFoundTheBucket = FALSE;
  1942. for (iBucket=0; iBucket < MAX_BUCKET && !bFoundTheBucket; iBucket++) {
  1943. if (FreeCount >= freeBuckets[iBucket].uLowerBound &&
  1944. FreeCount <= freeBuckets[iBucket].uUpperBound) {
  1945. //if it fits in this bucket:
  1946. //(a) increment the fragment count for this bucket
  1947. freeBuckets[iBucket].llFragCount++;
  1948. //(b) add up the size of the fragments in this bucket
  1949. freeBuckets[iBucket].llTotalFragSize += FreeCount;
  1950. //(c) add it into the total free space count
  1951. llTotalFreeClusters += FreeCount;
  1952. //(d) and count it on the total fragment count
  1953. llTotalFreeFragments++;
  1954. //then set the flag saying the buck for this
  1955. //free space was found. (the loop will then end)
  1956. bFoundTheBucket = TRUE;
  1957. }
  1958. }
  1959. //if we got here and it didn't fit in a bucket, just put it
  1960. //in the last bucket
  1961. if (!bFoundTheBucket) {
  1962. iBucket = MAX_BUCKET - 1;
  1963. freeBuckets[iBucket].llFragCount++;
  1964. freeBuckets[iBucket].llTotalFragSize += FreeCount;
  1965. llTotalFreeClusters += FreeCount;
  1966. llTotalFreeFragments++;
  1967. }
  1968. //dump the last bucket
  1969. if (iBucket == MAX_BUCKET - 1) {
  1970. Trace(log, " %8lu %8lu", (ULONG) FreeStartLcn, (ULONG) FreeCount);
  1971. }
  1972. } //end else (i.e. FoundCount != 0)
  1973. } // end while searchstartlcn != -1
  1974. //last bucket dump footers
  1975. Trace(log, " -------- --------", -1, NULL);
  1976. Trace(log, " total: %8lu", (ULONG) freeBuckets[MAX_BUCKET - 1].llTotalFragSize);
  1977. //ensure no division by zero
  1978. if (llTotalFreeClusters == 0) llTotalFreeClusters = 1;
  1979. if (llTotalFreeFragments == 0) llTotalFreeFragments = 1;
  1980. //print the statistics header
  1981. Trace(log, " ");
  1982. Trace(log, " Occurs x Totalled %% of %% of ");
  1983. Trace(log, " Freespace of sizes times clusters Fragments Free Space ");
  1984. Trace(log, " ------------------ -------- --------- --------- ----------- ");
  1985. //dump the data
  1986. for (iBucket = 0; iBucket<MAX_BUCKET; iBucket++) {
  1987. //compute the percentage this bucket is of the free fragments
  1988. llPercentOfFreeFragments = (1000000 * freeBuckets[iBucket].llFragCount) / llTotalFreeFragments;
  1989. //compute the percentage this bucket is of the free clusters
  1990. llPercentOfFreeSpace = (1000000 * freeBuckets[iBucket].llTotalFragSize) / llTotalFreeClusters;
  1991. //special case; if the bucket is the last one, just say "maximum" instead of the
  1992. //value as the upper bound, since that's how the code above works
  1993. if (iBucket == (MAX_BUCKET - 1)) {
  1994. wsprintf (cTemp, TEXT(" maximum"));
  1995. }
  1996. else {
  1997. wsprintf (cTemp, TEXT("%8lu"), (ULONG)freeBuckets[iBucket].uUpperBound);
  1998. }
  1999. //now we can print the detail line; note the quad percent signs, this
  2000. //is what we need in the boot-time code; it may not be needed elsewhere
  2001. Trace(log, " %8lu-%8s %8lu %8lu %3ld.%04ld%% %3ld.%04ld%% ",
  2002. (ULONG) freeBuckets[iBucket].uLowerBound,
  2003. cTemp,
  2004. (ULONG) freeBuckets[iBucket].llFragCount,
  2005. (ULONG) freeBuckets[iBucket].llTotalFragSize,
  2006. (LONG) llPercentOfFreeFragments/10000,
  2007. (LONG) llPercentOfFreeFragments%10000,
  2008. (LONG) llPercentOfFreeSpace/10000,
  2009. (LONG) llPercentOfFreeSpace%10000);
  2010. } //end the loop (iBucket=0->MAX_BUCKET-1)
  2011. // print some summary data
  2012. Trace(log, " ------------------ -------- --------- --------- -----------");
  2013. //such as the total clusters and fragments; this is a bit redundant, but if
  2014. //it's all printed, some self-consistency checks can be done (and bugs located)
  2015. Trace(log, " Totals: %8lu %8lu ",
  2016. (ULONG) llTotalFreeFragments,
  2017. (ULONG)llTotalFreeClusters);
  2018. //compute the percentage this bucket is of the free fragments
  2019. LONGLONG llUsableFreePercent = 100;
  2020. if (VolData.TotalClusters != 0) {
  2021. llUsableFreePercent = (1000000 * *llUsableFreeClusters) / VolData.TotalClusters;
  2022. }
  2023. //additionally, report the free space truly usable by the defragmenter
  2024. Trace(log, " ");
  2025. Trace(log, " Usable free space: %8lu clusters (%3ld.%04ld%% of volume space)",
  2026. (ULONG) *llUsableFreeClusters,
  2027. (LONG) llUsableFreePercent / 10000,
  2028. (LONG) llUsableFreePercent % 10000);
  2029. LONGLONG llCalcFreeClusters;
  2030. LONGLONG llCalcFreePercent = 100;
  2031. Trace(log, " Sum usable free space: %I64d clusters", llSumUsableFreeClusters);
  2032. Trace(log, " Sum unusable free space: %I64d clusters", llSumUnusableFreeClusters);
  2033. Trace(log, " Sum total free space: %I64d clusters", llSumTotalFreeClusters);
  2034. Trace(log, " ");
  2035. Trace(log, " Total free space: %I64d clusters", VolData.TotalClusters - VolData.UsedClusters);
  2036. Trace(log, " - MFT zone: %I64d clusters", VolData.MftZoneEnd - VolData.MftZoneStart);
  2037. Trace(log, " - sum unusable free space: %I64d clusters", llSumUnusableFreeClusters);
  2038. llCalcFreeClusters = VolData.TotalClusters - VolData.UsedClusters -
  2039. (VolData.MftZoneEnd - VolData.MftZoneStart) - llSumUnusableFreeClusters;
  2040. if (VolData.TotalClusters != 0) {
  2041. llCalcFreePercent = (1000000 * llCalcFreeClusters) / VolData.TotalClusters;
  2042. }
  2043. Trace(log, " = Usable free space: %I64d clusters (%3ld.%02ld%%)",
  2044. llCalcFreeClusters, (LONG) llCalcFreePercent / 10000, (LONG) llCalcFreePercent % 10000);
  2045. Trace(log, " ");
  2046. assert(llSumTotalFreeClusters == llSumUsableFreeClusters + llSumUnusableFreeClusters);
  2047. assert(llSumUsableFreeClusters == *llUsableFreeClusters);
  2048. if(VolData.hVolumeBitmap) {
  2049. GlobalUnlock(VolData.hVolumeBitmap);
  2050. }
  2051. Trace(log, "End: Determine UsableFreeSpace");
  2052. return TRUE;
  2053. }