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.

5535 lines
183 KiB

  1. /*****************************************************************************************************************
  2. FILENAME: DfrgNtfs.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. Scan Disk and/or defragment engine for NTFS volumes.
  5. If Analyze is specified on the command line, this will execute an analysis of the disk.
  6. If Defragment is specified on the command line, this will execute an analysis of the disk
  7. and then defragment it.
  8. */
  9. #ifndef INC_OLE2
  10. #define INC_OLE2
  11. #endif
  12. #include "stdafx.h"
  13. #define THIS_MODULE 'N'
  14. #define GLOBAL_DATAHOME
  15. #include <windows.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <commctrl.h>
  20. #include <winioctl.h>
  21. #include <shlobj.h> // for SHGetSpecialFolderLocation()
  22. #include "dfrgres.h"
  23. #include "DataIo.h"
  24. #include "DataIoCl.h"
  25. extern "C" {
  26. #include "SysStruc.h"
  27. }
  28. #include "ErrMacro.h"
  29. #include "Event.h"
  30. #include "DfrgCmn.h"
  31. #include "DfrgEngn.h"
  32. #include "DfrgRes.h"
  33. #include "DfrgNtfs.h"
  34. #include "DasdRead.h"
  35. #include "Extents.h"
  36. #include "FreeSpace.h"
  37. #include "FsSubs.h"
  38. #include "MoveFile.h"
  39. #include "NtfsSubs.h"
  40. #include "FraggedFileList.h"
  41. #include "Alloc.h"
  42. #include "DiskView.h"
  43. #include "Exclude.h"
  44. #include "GetReg.h"
  45. #include "GetTime.h"
  46. #include "IntFuncs.h"
  47. #include "Logging.h"
  48. #include "ErrMsg.h"
  49. #include "ErrLog.h"
  50. #include "Expand.h"
  51. #include "LogFile.h"
  52. #include "GetDfrgRes.h"
  53. extern "C" {
  54. #include "Priority.h"
  55. }
  56. #include "resource.h"
  57. #include <atlconv.h>
  58. #include "BootOptimizeNtfs.h"
  59. #include "mftdefrag.h"
  60. #include "defragcommon.h"
  61. static UINT DiskViewInterval = 1000; // default to 1 sec
  62. static HANDLE hDefragCompleteEvent = NULL;
  63. //This is set to terminate until the initialize has been successfully run.
  64. BOOL bTerminate = TRUE;
  65. BOOL bOCXIsDead = FALSE;
  66. BOOL bCommandLineUsed = FALSE;
  67. BOOL bLogFile = FALSE;
  68. BOOL bCommandLineMode = FALSE;
  69. BOOL bCommandLineForceFlag = FALSE;
  70. BOOL bCommandLineBootOptimizeFlag = FALSE;
  71. BOOL bDirtyVolume = FALSE;
  72. LPDATAOBJECT pdataDfrgCtl = NULL;
  73. static UINT uPass = 0;
  74. static UINT uPercentDone = 0;
  75. static UINT uLastPercentDone = 0;
  76. static UINT uDefragmentPercentDone = 0;
  77. static UINT uConsolidatePercentDone = 0;
  78. static UINT uEngineState = DEFRAG_STATE_NONE;
  79. TCHAR cWindow[100];
  80. static const DISKVIEW_TIMER_ID = 1;
  81. static const PING_TIMER_ID = 2;
  82. DiskView AnalyzeView;
  83. DiskView DefragView;
  84. //
  85. // --------------------- TABLE ALLOCATION/FREE ROUTINES ---------------------
  86. //
  87. /******************************************************************************
  88. ROUTINE DESCRIPTION:
  89. This allocates memory of size cbSize bytes. Note that cbSize MUST be the
  90. size we're expecting it to be (based on the slab-allocator initialisation),
  91. since our slab allocator can only handle packets of one size.
  92. INPUT:
  93. pTable - The table that the comparison is being made for (not used)
  94. cbSize - The count in bytes of the memory needed
  95. RETURN:
  96. Pointer to allocated memory of size cbSize; NULL if the system is out
  97. of memory, or cbSize is not what the slab allocator was initialised with.
  98. */
  99. PVOID
  100. NTAPI
  101. FreeSpaceAllocateRoutine(
  102. IN PRTL_GENERIC_TABLE pTable,
  103. IN CLONG cbSize
  104. )
  105. {
  106. PVOID pMemory = NULL;
  107. //
  108. // Sanity-check to make sure that we're being asked for packets of the
  109. // "correct" size, since our slab-allocator can only deal with packets
  110. // of a given size
  111. //
  112. if ((cbSize + sizeof(PVOID)) == VolData.SaFreeSpaceContext.dwPacketSize) {
  113. //
  114. // size was correct; call our allocator
  115. //
  116. pMemory = SaAllocatePacket(&VolData.SaFreeSpaceContext);
  117. }
  118. else {
  119. //
  120. // Oops, we have a problem!
  121. //
  122. Trace(error, "Internal Error. FreeSpaceAllocateRoutine called with "
  123. "unexpected size (%lu instead of %lu).",
  124. cbSize, VolData.SaFreeSpaceContext.dwPacketSize - sizeof(PVOID));
  125. assert(FALSE);
  126. }
  127. return pMemory;
  128. UNREFERENCED_PARAMETER(pTable);
  129. }
  130. /******************************************************************************
  131. ROUTINE DESCRIPTION:
  132. This allocates memory of size cbSize bytes. Note that cbSize MUST be the
  133. size we're expecting it to be (based on the slab-allocator initialisation),
  134. since our slab allocator can only handle packets of one size.
  135. INPUT:
  136. pTable - The table that the comparison is being made for (not used)
  137. cbSize - The count in bytes of the memory needed
  138. RETURN:
  139. Pointer to allocated memory of size cbSize; NULL if the system is out
  140. of memory, or cbSize is not what the slab allocator was initialised with.
  141. */
  142. PVOID
  143. NTAPI
  144. FileEntryAllocateRoutine(
  145. IN PRTL_GENERIC_TABLE pTable,
  146. IN CLONG cbSize
  147. )
  148. {
  149. PVOID pMemory = NULL;
  150. //
  151. // Sanity-check to make sure that we're being asked for packets of the
  152. // "correct" size, since our slab-allocator can only deal with packets
  153. // of a given size
  154. //
  155. if ((cbSize + sizeof(PVOID)) == VolData.SaFileEntryContext.dwPacketSize) {
  156. //
  157. // size was correct; call our allocator
  158. //
  159. pMemory = SaAllocatePacket(&VolData.SaFileEntryContext);
  160. }
  161. else {
  162. //
  163. // Oops, we have a problem!
  164. //
  165. Trace(error, "Internal Error. FileEntryAllocateRoutine called with "
  166. "unexpected size (%lu instead of %lu).",
  167. cbSize, VolData.SaFileEntryContext.dwPacketSize - sizeof(PVOID));
  168. assert(FALSE);
  169. }
  170. return pMemory;
  171. UNREFERENCED_PARAMETER(pTable);
  172. }
  173. /******************************************************************************
  174. ROUTINE DESCRIPTION:
  175. This frees a packet allocated by BootOptimiseAllocateRoutine
  176. INPUT:
  177. pTable - The table that the comparison is being made for (not used)
  178. pvBuffer - Pointer to the memory to be freed. This pointer should not
  179. be used after this routine is called.
  180. RETURN:
  181. VOID
  182. */
  183. VOID
  184. NTAPI
  185. FreeSpaceFreeRoutine(
  186. IN PRTL_GENERIC_TABLE pTable,
  187. IN PVOID pvBuffer
  188. )
  189. {
  190. assert(pvBuffer);
  191. SaFreePacket(&VolData.SaFreeSpaceContext, pvBuffer);
  192. UNREFERENCED_PARAMETER(pTable);
  193. }
  194. /******************************************************************************
  195. ROUTINE DESCRIPTION:
  196. This frees a packet allocated by BootOptimiseAllocateRoutine
  197. INPUT:
  198. pTable - The table that the comparison is being made for (not used)
  199. pvBuffer - Pointer to the memory to be freed. This pointer should not
  200. be used after this routine is called.
  201. RETURN:
  202. VOID
  203. */
  204. VOID
  205. NTAPI
  206. FileEntryFreeRoutine(
  207. IN PRTL_GENERIC_TABLE pTable,
  208. IN PVOID pvBuffer
  209. )
  210. {
  211. assert(pvBuffer);
  212. return SaFreePacket(&VolData.SaFileEntryContext, pvBuffer);
  213. UNREFERENCED_PARAMETER(pTable);
  214. }
  215. //
  216. // --------------------- TABLE COMPARE ROUTINES ---------------------
  217. //
  218. /******************************************************************************
  219. ROUTINE DESCRIPTION:
  220. Comparison routine to compare the two FREE_SPACE_ENTRY records. If
  221. the SortBySize flag is set to TRUE in either of the records, the comparison
  222. is based on the ClusterCount (with the StartingLcn as the secondary key),
  223. else it is based on the StartingLcn.
  224. INPUT:
  225. pTable - the table that the comparison is being made for (not used)
  226. pNode1 - the first FREE_SPACE_ENTRY to be compared
  227. pNode2 - the second FREE_SPACE_ENTRY to be compared
  228. RETURN:
  229. RtlGenericLessThan if pNode1 < pNode2
  230. RtlGenericGreaterThan if pNode1 > pNode2
  231. RtlGenericEqual if pNode1 == pNode2
  232. */
  233. RTL_GENERIC_COMPARE_RESULTS
  234. NTAPI
  235. FreeSpaceCompareRoutine(
  236. PRTL_GENERIC_TABLE pTable,
  237. PVOID pNode1,
  238. PVOID pNode2
  239. )
  240. {
  241. PFREE_SPACE_ENTRY pEntry1 = (PFREE_SPACE_ENTRY) pNode1;
  242. PFREE_SPACE_ENTRY pEntry2 = (PFREE_SPACE_ENTRY) pNode2;
  243. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  244. //
  245. // These shouldn't ever be NULL
  246. //
  247. assert(pNode1 && pNode2);
  248. if ((pEntry1->SortBySize) || (pEntry2->SortBySize)) {
  249. //
  250. // If one of the nodes thinks that the table is sorted by
  251. // size, they better both think so!
  252. //
  253. assert(pEntry1->SortBySize && pEntry2->SortBySize);
  254. if (pEntry1->ClusterCount > pEntry2->ClusterCount) {
  255. //
  256. // The first node is bigger than the second
  257. //
  258. result = GenericGreaterThan;
  259. }
  260. else if (pEntry1->ClusterCount < pEntry2->ClusterCount) {
  261. //
  262. // The first node is smaller than the second
  263. //
  264. result = GenericLessThan;
  265. }
  266. else {
  267. //
  268. // Multiple freespaces may have the same length--so let's
  269. // use the StartingLcn as the unique key.
  270. //
  271. if (pEntry1->StartingLcn > pEntry2->StartingLcn) {
  272. result = GenericGreaterThan;
  273. }
  274. else if (pEntry1->StartingLcn < pEntry2->StartingLcn) {
  275. result = GenericLessThan;
  276. }
  277. }
  278. }
  279. else {
  280. //
  281. // Sort by Starting LCN
  282. //
  283. if (pEntry1->StartingLcn > pEntry2->StartingLcn) {
  284. result = GenericGreaterThan;
  285. }
  286. else if (pEntry1->StartingLcn < pEntry2->StartingLcn) {
  287. result = GenericLessThan;
  288. }
  289. }
  290. //
  291. // Default is GenericEqual
  292. //
  293. return result;
  294. UNREFERENCED_PARAMETER(pTable);
  295. }
  296. /******************************************************************************
  297. ROUTINE DESCRIPTION:
  298. Routine to compare two FILE_LIST_ENTRY records, based on the ClusterCount,
  299. with the FileRecordNumber as the secondary key.
  300. INPUT:
  301. pTable - the table that the comparison is being made for (not used)
  302. pNode1 - the first FILE_LIST_ENTRY to be compared
  303. pNode2 - the second FILE_LIST_ENTRY to be compared
  304. RETURN:
  305. RtlGenericLessThan if pNode1 < pNode2
  306. RtlGenericGreaterThan if pNode1 > pNode2
  307. RtlGenericEqual if pNode1 == pNode2
  308. */
  309. RTL_GENERIC_COMPARE_RESULTS
  310. NTAPI
  311. FileEntrySizeCompareRoutine(
  312. PRTL_GENERIC_TABLE pTable,
  313. PVOID pNode1,
  314. PVOID pNode2
  315. )
  316. {
  317. PFILE_LIST_ENTRY pEntry1 = (PFILE_LIST_ENTRY) pNode1;
  318. PFILE_LIST_ENTRY pEntry2 = (PFILE_LIST_ENTRY) pNode2;
  319. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  320. //
  321. // These shouldn't ever be NULL
  322. //
  323. assert(pNode1 && pNode2);
  324. //
  325. // If the FRN is the same, this is the same record
  326. //
  327. if (pEntry1->FileRecordNumber != pEntry2->FileRecordNumber) {
  328. // Sort by ClusterCount
  329. if (pEntry1->ClusterCount < pEntry2->ClusterCount) {
  330. result = GenericLessThan;
  331. }
  332. else if (pEntry1->ClusterCount > pEntry2->ClusterCount) {
  333. result = GenericGreaterThan;
  334. }
  335. else {
  336. //
  337. // Multiple files may have the same cluster count--so let's
  338. // use the FileRecordNumber as the secondary key.
  339. //
  340. if (pEntry1->FileRecordNumber > pEntry2->FileRecordNumber) {
  341. result = GenericGreaterThan;
  342. }
  343. else {
  344. result = GenericLessThan;
  345. }
  346. }
  347. }
  348. //
  349. // Default is GenericEqual
  350. //
  351. return result;
  352. UNREFERENCED_PARAMETER(pTable);
  353. }
  354. /******************************************************************************
  355. ROUTINE DESCRIPTION:
  356. Comparison routine to compare the StartingLcn of two FILE_LIST_ENTRY
  357. records.
  358. INPUT:
  359. pTable - the table that the comparison is being made for (not used)
  360. pNode1 - the first FILE_LIST_ENTRY to be compared
  361. pNode2 - the second FILE_LIST_ENTRY to be compared
  362. RETURN:
  363. RtlGenericLessThan if pNode1 < pNode2
  364. RtlGenericGreaterThan if pNode1 > pNode2
  365. RtlGenericEqual if pNode1 == pNode2
  366. */
  367. RTL_GENERIC_COMPARE_RESULTS
  368. NTAPI
  369. FileEntryStartLcnCompareRoutine(
  370. PRTL_GENERIC_TABLE pTable,
  371. PVOID pNode1,
  372. PVOID pNode2
  373. )
  374. {
  375. PFILE_LIST_ENTRY pEntry1 = (PFILE_LIST_ENTRY) pNode1;
  376. PFILE_LIST_ENTRY pEntry2 = (PFILE_LIST_ENTRY) pNode2;
  377. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  378. //
  379. // These shouldn't ever be NULL
  380. //
  381. assert(pNode1 && pNode2);
  382. //
  383. // If the FRN is the same, this is the same record
  384. //
  385. if (pEntry1->FileRecordNumber != pEntry2->FileRecordNumber) {
  386. //
  387. // FRN is different, compare based on starting LCN in REVERSE order
  388. //
  389. if (pEntry1->StartingLcn < pEntry2->StartingLcn) {
  390. result = GenericGreaterThan;
  391. }
  392. else if (pEntry1->StartingLcn > pEntry2->StartingLcn) {
  393. result = GenericLessThan;
  394. }
  395. }
  396. //
  397. // Default is GenericEqual
  398. //
  399. return result;
  400. UNREFERENCED_PARAMETER(pTable);
  401. }
  402. /******************************************************************************
  403. ROUTINE DESCRIPTION:
  404. Comparison routine to compare the ExcessExtentCount of two FILE_LIST_ENTRY
  405. records, using StartingLcn as the secondary key.
  406. INPUT:
  407. pTable - the table that the comparison is being made for (not used)
  408. pNode1 - the first FILE_LIST_ENTRY to be compared
  409. pNode2 - the second FILE_LIST_ENTRY to be compared
  410. RETURN:
  411. RtlGenericLessThan if pNode1 < pNode2
  412. RtlGenericGreaterThan if pNode1 > pNode2
  413. RtlGenericEqual if pNode1 == pNode2
  414. */
  415. RTL_GENERIC_COMPARE_RESULTS
  416. NTAPI
  417. FileEntryNumFragmentsCompareRoutine(
  418. PRTL_GENERIC_TABLE pTable,
  419. PVOID pNode1,
  420. PVOID pNode2
  421. )
  422. {
  423. PFILE_LIST_ENTRY pEntry1 = (PFILE_LIST_ENTRY) pNode1;
  424. PFILE_LIST_ENTRY pEntry2 = (PFILE_LIST_ENTRY) pNode2;
  425. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  426. //
  427. // These shouldn't ever be NULL
  428. //
  429. assert(pNode1 && pNode2);
  430. //
  431. // If the FRN is the same, this is the same record
  432. //
  433. if (pEntry1->FileRecordNumber != pEntry2->FileRecordNumber) {
  434. //
  435. // FRN is different, compare based on number of fragments. Note that
  436. // if the number of fragments are the same, we cannot return
  437. // GenericEqual (since these are different files (different FRN's)),
  438. // and we use the StartingLcn as the secondary key.
  439. //
  440. if (pEntry1->ExcessExtentCount > pEntry2->ExcessExtentCount) {
  441. result = GenericGreaterThan;
  442. }
  443. else if (pEntry1->ExcessExtentCount < pEntry2->ExcessExtentCount) {
  444. result = GenericLessThan;
  445. }
  446. else if (pEntry1->StartingLcn > pEntry2->StartingLcn) {
  447. result = GenericGreaterThan;
  448. }
  449. else {
  450. result = GenericLessThan;
  451. }
  452. }
  453. //
  454. // Default is GenericEqual
  455. //
  456. return result;
  457. UNREFERENCED_PARAMETER(pTable);
  458. }
  459. /****************************************************************************************************************
  460. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  461. ROUTINE DESCRIPTION:
  462. Allocates memory for the file lists.
  463. INPUT + OUTPUT:
  464. fAnalyseOnly: This flag indicates if the tree is being set up for analysis
  465. GLOBALS:
  466. IN OUT VolData.NonMovableFileTable - Nonmovable file list
  467. IN OUT VolData.FragmentedFileTable - Fragmented file list
  468. IN OUT VolData.CongituousFileTable - Congituous file list
  469. RETURN:
  470. TRUE - Success.
  471. FALSE - Fatal Error.
  472. */
  473. BOOL
  474. AllocateFileLists(
  475. IN CONST BOOL fAnalyseOnly
  476. )
  477. {
  478. PVOID pTableContext = NULL;
  479. Trace(log, "Initializing file tables for %s", (fAnalyseOnly ? "analysis" : "defragmentation"));
  480. if (!SaInitialiseContext(&VolData.SaFileEntryContext, sizeof(FILE_LIST_ENTRY), 64*1024)) {
  481. return FALSE;
  482. }
  483. if (fAnalyseOnly) {
  484. //
  485. // Analyse only: Set up the AVL tree that will hold the fragmented file list
  486. // Note that we sort here by NumFragments
  487. //
  488. pTableContext = NULL;
  489. RtlInitializeGenericTable(&VolData.FragmentedFileTable,
  490. FileEntryNumFragmentsCompareRoutine,
  491. FileEntryAllocateRoutine,
  492. FileEntryFreeRoutine,
  493. pTableContext);
  494. }
  495. else {
  496. if (!SaInitialiseContext(&VolData.SaFreeSpaceContext, sizeof(FREE_SPACE_ENTRY), 64*1024)) {
  497. return FALSE;
  498. }
  499. //
  500. // Set up the AVL tree that will hold the non-movable file list
  501. //
  502. Trace(log, "Initializing file tables ");
  503. RtlInitializeGenericTable(&VolData.NonMovableFileTable,
  504. FileEntryStartLcnCompareRoutine,
  505. FileEntryAllocateRoutine,
  506. FileEntryFreeRoutine,
  507. pTableContext);
  508. //
  509. // Set up the AVL tree that will hold the fragmented file list
  510. //
  511. pTableContext = NULL;
  512. RtlInitializeGenericTable(&VolData.FragmentedFileTable,
  513. FileEntrySizeCompareRoutine,
  514. FileEntryAllocateRoutine,
  515. FileEntryFreeRoutine,
  516. pTableContext);
  517. //
  518. // Set up the AVL tree that will hold the contiguous file list
  519. //
  520. pTableContext = NULL;
  521. RtlInitializeGenericTable(&VolData.ContiguousFileTable,
  522. FileEntryStartLcnCompareRoutine,
  523. FileEntryAllocateRoutine,
  524. FileEntryFreeRoutine,
  525. pTableContext);
  526. //
  527. // Finally, set up the AVL tree that will hold the free-space list
  528. //
  529. pTableContext = NULL;
  530. RtlInitializeGenericTable(&VolData.FreeSpaceTable,
  531. FreeSpaceCompareRoutine,
  532. FreeSpaceAllocateRoutine,
  533. FreeSpaceFreeRoutine,
  534. pTableContext);
  535. }
  536. return TRUE;
  537. }
  538. /******************************************************************************
  539. ROUTINE DESCRIPTION:
  540. Routine to allocate the multiple free-space lists, that are sorted by
  541. StartingLcn (but grouped by size).
  542. INPUT+OUTPUT:
  543. VolData.MultipleFreeSpaceTrees
  544. RETURN:
  545. Void
  546. */
  547. BOOL
  548. AllocateFreeSpaceListsWithMultipleTrees()
  549. {
  550. DWORD dwTableIndex = 0;
  551. PVOID pTableContext = NULL;
  552. // Initialise the tables
  553. do {
  554. pTableContext = NULL;
  555. RtlInitializeGenericTable(&(VolData.MultipleFreeSpaceTrees[dwTableIndex]),
  556. FreeSpaceCompareRoutine,
  557. FreeSpaceAllocateRoutine,
  558. FreeSpaceFreeRoutine,
  559. pTableContext);
  560. } while (++dwTableIndex < 10);
  561. return TRUE;
  562. }
  563. /******************************************************************************
  564. ROUTINE DESCRIPTION:
  565. Routine to clear VolData.FreeSpaceTable.
  566. INPUT+OUTPUT:
  567. VolData.FreeSpaceTable
  568. RETURN:
  569. Void
  570. */
  571. VOID
  572. ClearFreeSpaceTable()
  573. {
  574. PVOID pTableContext = NULL;
  575. // Release all references to allocated memory
  576. VolData.pFreeSpaceEntry = NULL;
  577. // Re-initialise the table
  578. RtlInitializeGenericTable(&VolData.FreeSpaceTable,
  579. FreeSpaceCompareRoutine,
  580. FreeSpaceAllocateRoutine,
  581. FreeSpaceFreeRoutine,
  582. pTableContext);
  583. // And free the allocated memory
  584. SaFreeAllPackets(&VolData.SaFreeSpaceContext);
  585. }
  586. /******************************************************************************
  587. ROUTINE DESCRIPTION:
  588. Routine to clear VolData.MultipleFreeSpaceTrees.
  589. INPUT+OUTPUT:
  590. VolData.MultipleFreeSpaceTrees
  591. RETURN:
  592. Void
  593. */
  594. VOID
  595. ClearFreeSpaceListWithMultipleTrees()
  596. {
  597. DWORD dwTableIndex = 0;
  598. PVOID pTableContext = NULL;
  599. // Release all references to allocated memory
  600. VolData.pFreeSpaceEntry = NULL;
  601. // Re-initialise each of the tables
  602. do {
  603. pTableContext = NULL;
  604. RtlInitializeGenericTable(&(VolData.MultipleFreeSpaceTrees[dwTableIndex]),
  605. FreeSpaceCompareRoutine,
  606. FreeSpaceAllocateRoutine,
  607. FreeSpaceFreeRoutine,
  608. pTableContext);
  609. } while (++dwTableIndex < 10);
  610. // And free the allocated memory
  611. SaFreeAllPackets(&VolData.SaFreeSpaceContext);
  612. }
  613. /*******************************************************************************
  614. ROUTINE DESCRIPTION:
  615. This is the WinMain function for the NTFS defragmention engine.
  616. INPUT + OUTPUT:
  617. IN hInstance - The handle to this instance.
  618. IN hPrevInstance - The handle to the previous instance.
  619. IN lpCmdLine - The command line which was passed in.
  620. IN nCmdShow - Whether the window should be minimized or not.
  621. GLOBALS:
  622. OUT AnalyzeOrDefrag - Tells whether were supposed to to an analyze or an
  623. analyze and a defrag.
  624. OUT VolData.cDrive - The drive letter with a colon after it.
  625. RETURN:
  626. FALSE - Failure to initilize.
  627. other - Various values can return at exit.
  628. */
  629. int APIENTRY
  630. WinMain(
  631. IN HINSTANCE hInstance,
  632. IN HINSTANCE hPrevInstance,
  633. IN LPSTR lpCmdLine,
  634. IN int nCmdShow
  635. )
  636. {
  637. WNDCLASS wc;
  638. MSG Message;
  639. HRESULT hr = E_FAIL;
  640. //0.0E00 Before we start using VolData, zero it out.
  641. ZeroMemory(&VolData, sizeof(VOL_DATA));
  642. CoInitializeEx(NULL, COINIT_MULTITHREADED);
  643. /*
  644. Commenting this call out to minimise registry changes for XP SP 1.
  645. // Initialize COM security
  646. hr = CoInitializeSecurity(
  647. (PVOID)&CLSID_DfrgNtfs, // IN PSECURITY_DESCRIPTOR pSecDesc,
  648. -1, // IN LONG cAuthSvc,
  649. NULL, // IN SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
  650. NULL, // IN void *pReserved1,
  651. RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // IN DWORD dwAuthnLevel,
  652. RPC_C_IMP_LEVEL_IDENTIFY, // IN DWORD dwImpLevel,
  653. NULL, // IN void *pAuthList,
  654. (EOAC_SECURE_REFS | EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL | EOAC_APPID),
  655. NULL // IN void *pReserved3
  656. );
  657. if(FAILED(hr)) {
  658. return 0;
  659. }
  660. */
  661. // get the Instance to the resource DLL
  662. // error text is from the local resources
  663. if (GetDfrgResHandle() == NULL){
  664. //took out the display error message stuff from here because we needed to
  665. //not display error dialogs for the commandline, but here is the problem...
  666. //we have not got the information from InitializeDrive yet, so we don't
  667. //know what mode we are in, so we can't either write to a log or display
  668. //a message box. The likelyhood that this call will fail is very small, so
  669. //not doing anything is not a problem. One other problem exist here that
  670. //I am not even going to try and solve, and that is when this call fails,
  671. //since the COM server is not set up correctly, we go off into never never
  672. //land and cause a server busy dialog to be displayed by the system, not
  673. //from defrag. Scott K. Sipe
  674. return FALSE;
  675. }
  676. OSVERSIONINFO Ver;
  677. //This should only work on version 5 or later. Do a check.
  678. Ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  679. EF(GetVersionEx(&Ver));
  680. if(Ver.dwMajorVersion < 5){
  681. //took out the display error message stuff from here because we needed to
  682. //not display error dialogs for the commandline, but here is the problem...
  683. //we have not got the information from InitializeDrive yet, so we don't
  684. //know what mode we are in, so we can't either write to a log or display
  685. //a message box. The likelyhood that this call will fail is very small, so
  686. //not doing anything is not a problem. One other problem exist here that
  687. //I am not even going to try and solve, and that is when this call fails,
  688. //since the COM server is not set up correctly, we go off into never never
  689. //land and cause a server busy dialog to be displayed by the system, not
  690. //from defrag. Scott K. Sipe
  691. return FALSE;
  692. }
  693. //0.0E00 Build the window name from the drive letter.
  694. wcscpy(cWindow, DFRGNTFS_WINDOW);
  695. //0.0E00 Initialize the window class.
  696. wc.style = CS_OWNDC;
  697. wc.lpfnWndProc = (WNDPROC) MainWndProc;
  698. wc.cbClsExtra = 0;
  699. wc.cbWndExtra = sizeof(PVOID);
  700. wc.hInstance = hInstance;
  701. wc.hIcon = NULL;
  702. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  703. wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  704. wc.lpszMenuName = NULL;
  705. wc.lpszClassName = DFRGNTFS_CLASS;
  706. //0.0E00 Register the window class.
  707. if(!RegisterClass(&wc)){
  708. //took out the display error message stuff from here because we needed to
  709. //not display error dialogs for the commandline, but here is the problem...
  710. //we have not got the information from InitializeDrive yet, so we don't
  711. //know what mode we are in, so we can't either write to a log or display
  712. //a message box. The likelyhood that this call will fail is very small, so
  713. //not doing anything is not a problem. One other problem exist here that
  714. //I am not even going to try and solve, and that is when this call fails,
  715. //since the COM server is not set up correctly, we go off into never never
  716. //land and cause a server busy dialog to be displayed by the system, not
  717. //from defrag. Scott K. Sipe
  718. return FALSE;
  719. }
  720. //0.0E00 Create the window.
  721. if((hwndMain = CreateWindow(DFRGNTFS_CLASS,
  722. cWindow,
  723. WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL | WS_MINIMIZE,
  724. CW_USEDEFAULT,
  725. CW_USEDEFAULT,
  726. CW_USEDEFAULT,
  727. CW_USEDEFAULT,
  728. NULL,
  729. NULL,
  730. hInstance,
  731. (LPVOID)IntToPtr(NULL))) == NULL){
  732. //took out the display error message stuff from here because we needed to
  733. //not display error dialogs for the commandline, but here is the problem...
  734. //we have not got the information from InitializeDrive yet, so we don't
  735. //know what mode we are in, so we can't either write to a log or display
  736. //a message box. The likelyhood that this call will fail is very small, so
  737. //not doing anything is not a problem. One other problem exist here that
  738. //I am not even going to try and solve, and that is when this call fails,
  739. //since the COM server is not set up correctly, we go off into never never
  740. //land and cause a server busy dialog to be displayed by the system, not
  741. //from defrag. Scott K. Sipe
  742. return FALSE;
  743. }
  744. //0.0E00 PostMessage for ID_INITALIZE which will get data about the volume, etc.
  745. SendMessage (hwndMain, WM_COMMAND, ID_INITIALIZE, 0);
  746. //0.0E00 Pass any posted messages on to MainWndProc.
  747. while(GetMessage(&Message, NULL, 0, 0)){
  748. TranslateMessage(&Message);
  749. DispatchMessage(&Message);
  750. }
  751. return (int) Message.wParam;
  752. }
  753. /*******************************************************************************
  754. ROUTINE DESCRIPTION:
  755. This module carries out all initialization before the Analyze or Defrag
  756. threads start.
  757. INPUT + OUTPUT:
  758. None.
  759. GLOBALS:
  760. IN OUT Various VolData fields.
  761. RETURN:
  762. TRUE - Success.
  763. FALSE - Fatal Error.
  764. */
  765. BOOL
  766. Initialize(
  767. )
  768. {
  769. //0.0E00 Initialize a message window.
  770. InitCommonControls();
  771. // Initialize DCOM DataIo communication.
  772. InitializeDataIo(CLSID_DfrgNtfs, REGCLS_SINGLEUSE);
  773. return TRUE;
  774. }
  775. /*******************************************************************************
  776. ROUTINE DESCRIPTION:
  777. Sends the status data (including percent complete and file being processed)
  778. to the GUI.
  779. INPUT + OUTPUT:
  780. None
  781. GLOBALS:
  782. This routine uses the following globals:
  783. uPass
  784. uPercentDone
  785. uEngineState
  786. VolData.cVolumeName
  787. VolData.vFileName
  788. RETURN:
  789. None.
  790. */
  791. VOID
  792. SendStatusData(
  793. )
  794. {
  795. STATUS_DATA statusData = {0};
  796. if (uPercentDone < uLastPercentDone) {
  797. uPercentDone = uLastPercentDone;
  798. }
  799. uLastPercentDone = uPercentDone;
  800. statusData.dwPass = uPass;
  801. statusData.dwPercentDone = (uPercentDone > 100 ? 100 : uPercentDone);
  802. statusData.dwEngineState = uEngineState;
  803. //pStatusData->cDrive = VolData.cDrive[0];
  804. _tcsncpy(statusData.cVolumeName, VolData.cVolumeName,GUID_LENGTH);
  805. if(VolData.vFileName.GetLength() > 0)
  806. {
  807. _tcsncpy(statusData.vsFileName, VolData.vFileName.GetBuffer(),200);
  808. }
  809. //If the gui is connected, send gui data to it.
  810. DataIoClientSetData(ID_STATUS, (TCHAR*)&statusData, sizeof(STATUS_DATA), pdataDfrgCtl);
  811. }
  812. /*******************************************************************************
  813. ROUTINE DESCRIPTION:
  814. This is the WndProc function for the NTFS defragmentaion engine.
  815. GLOBALS:
  816. IN AnalyzeOrDefrag - Holds a define for whether the current run is an
  817. analysis or defragmentation run.
  818. hThread - Handle to the worker thread (either analyze or defrag).
  819. INPUT:
  820. hWnd - Handle to the window.
  821. uMsg - The message.
  822. wParam - The word parameter for the message.
  823. lParam - the long parameter for the message.
  824. RETURN:
  825. various.
  826. */
  827. LRESULT CALLBACK
  828. MainWndProc(
  829. IN HWND hWnd,
  830. IN UINT uMsg,
  831. IN WPARAM wParam,
  832. IN LPARAM lParam
  833. )
  834. {
  835. DATA_IO* pDataIo;
  836. switch(uMsg) {
  837. case WM_COMMAND:
  838. switch(LOWORD(wParam)) {
  839. case ID_INIT_VOLUME_COMM:
  840. {
  841. USES_CONVERSION;
  842. CLSID clsidVolume;
  843. HRESULT hr = E_FAIL;
  844. //
  845. // Get the volume comm id out of the given data.
  846. //
  847. pDataIo = (DATA_IO*) GlobalLock((void*)lParam);
  848. if (!pDataIo) {
  849. LOG_ERR();
  850. assert(FALSE);
  851. break;
  852. }
  853. hr = CLSIDFromString(T2OLE((PTCHAR)&pDataIo->cData), &clsidVolume);
  854. if (FAILED(hr)) {
  855. LOG_ERR();
  856. assert(FALSE);
  857. break;
  858. }
  859. //
  860. // Initialize the upstream communication given the
  861. // guid.
  862. //
  863. InitializeDataIoClient(clsidVolume, NULL, &pdataDfrgCtl);
  864. break;
  865. }
  866. case ID_INITIALIZE:
  867. {
  868. Initialize();
  869. #ifdef CMDLINE
  870. #pragma message ("Information: CMDLINE defined.")
  871. //0.0E00 Get the command line passed in.
  872. PTCHAR pCommandLine = GetCommandLine();
  873. // if "-Embed..." is NOT found in the string, then this was a command line
  874. // submitted by the user and NOT by the OCX. Package it up and send it to the
  875. // message pump. If -Embed was found, the OCX will send the command line in
  876. // a ID_INITIALIZE_DRIVE message.
  877. if (_tcsstr(pCommandLine, TEXT("-Embed")) == NULL){
  878. HANDLE hCommandLine = NULL;
  879. DATA_IO* pCmdLine = NULL;
  880. //If this is not called by the MMC, use the command line typed in from the DOS window.
  881. bCommandLineUsed = TRUE;
  882. AllocateMemory((lstrlen(pCommandLine)+1)*sizeof(TCHAR)+sizeof(DATA_IO), &hCommandLine, (void**)&pCmdLine);
  883. EB(hCommandLine);
  884. lstrcpy(&pCmdLine->cData, pCommandLine);
  885. GlobalUnlock(hCommandLine);
  886. PostMessage(hWnd, WM_COMMAND, ID_INITIALIZE_DRIVE, (LPARAM)hCommandLine);
  887. }
  888. #else
  889. #pragma message ("Information: CMDLINE not defined.")
  890. //0.0E00 Get the command line passed in.
  891. PTCHAR pCommandLine = GetCommandLine();
  892. // if "-Embed..." is NOT found in the string, then this was a command line
  893. // submitted by the user and NOT by the OCX and that is not supported.
  894. // Raise an error dialog and send an ABORT to the engine
  895. if (_tcsstr(pCommandLine, TEXT("-Embed")) == NULL){
  896. VString msg(IDS_CMD_LINE_OFF, GetDfrgResHandle());
  897. VString title(IDS_DK_TITLE, GetDfrgResHandle());
  898. if (msg.IsEmpty() == FALSE) {
  899. MessageBox(NULL, msg.GetBuffer(), title.GetBuffer(), MB_OK|MB_ICONSTOP);
  900. }
  901. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  902. }
  903. #endif
  904. break;
  905. }
  906. case ID_INITIALIZE_DRIVE:
  907. Trace(log, "Received ID_INITIALIZE_DRIVE");
  908. pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
  909. if(!InitializeDrive((PTCHAR)&pDataIo->cData)){
  910. //0.0E00 If initialize failed, pop up a message box, log an abort, and trigger an abort.
  911. //IDS_SCANNTFS_INIT_ABORT - "ScanNTFS: Initialize Aborted - Fatal Error"
  912. VString msg(IDS_SCANNTFS_INIT_ABORT, GetDfrgResHandle());
  913. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  914. //0.0E00 Log an abort in the event log.
  915. LogEvent(MSG_ENGINE_ERROR, msg.GetBuffer());
  916. //0.0E00 Trigger an abort.
  917. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  918. // set the event to signaled, allowing the UI to proceed
  919. if (hDefragCompleteEvent){
  920. SetEvent(hDefragCompleteEvent);
  921. }
  922. }
  923. EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
  924. EH_ASSERT(GlobalFree((void*)lParam) == NULL);
  925. break;
  926. case ID_ANALYZE:
  927. //0.0E00 Create an analyze thread.
  928. {
  929. DWORD ThreadId;
  930. hThread = CreateThread(
  931. NULL,
  932. 0,
  933. (LPTHREAD_START_ROUTINE)AnalyzeThread,
  934. NULL,
  935. 0,
  936. &ThreadId);
  937. if (NULL == hThread) {
  938. LOG_ERR();
  939. assert(FALSE);
  940. break;
  941. }
  942. }
  943. break;
  944. case ID_DEFRAG:
  945. //0.0E00 Create a defrag thread.
  946. {
  947. DWORD ThreadId;
  948. hThread = CreateThread(
  949. NULL,
  950. 0,
  951. (LPTHREAD_START_ROUTINE)DefragThread,
  952. NULL,
  953. 0,
  954. &ThreadId);
  955. if (NULL == hThread) {
  956. LOG_ERR();
  957. assert(FALSE);
  958. break;
  959. }
  960. }
  961. break;
  962. case ID_STOP:
  963. {
  964. Trace(log, "Received ID_STOP");
  965. //Tell the worker thread to terminate.
  966. VolData.EngineState = TERMINATE;
  967. //Send status data to the UI.
  968. SendStatusData();
  969. break;
  970. }
  971. case ID_PAUSE_ON_SNAPSHOT:
  972. {
  973. #ifndef NOTIMER
  974. KillTimer(hwndMain, PING_TIMER_ID);
  975. #endif
  976. NOT_DATA NotData;
  977. wcscpy(NotData.cVolumeName, VolData.cVolumeName);
  978. Trace(log, "Received ID_PAUSE_ON_SNAPSHOT");
  979. VolData.EngineState = PAUSED;
  980. // Tell the UI we've paused.
  981. DataIoClientSetData(
  982. ID_PAUSE_ON_SNAPSHOT,
  983. (PTCHAR)&NotData,
  984. sizeof(NOT_DATA),
  985. pdataDfrgCtl
  986. );
  987. break;
  988. }
  989. case ID_PAUSE:
  990. {
  991. #ifndef NOTIMER
  992. KillTimer(hwndMain, PING_TIMER_ID);
  993. #endif
  994. NOT_DATA NotData;
  995. wcscpy(NotData.cVolumeName, VolData.cVolumeName);
  996. Trace(log, "Received ID_PAUSE");
  997. VolData.EngineState = PAUSED;
  998. // Tell the UI we've paused.
  999. DataIoClientSetData(
  1000. ID_PAUSE,
  1001. (PTCHAR)&NotData,
  1002. sizeof(NOT_DATA),
  1003. pdataDfrgCtl
  1004. );
  1005. break;
  1006. }
  1007. case ID_CONTINUE:
  1008. {
  1009. #ifndef NOTIMER
  1010. EF_ASSERT(SetTimer(hwndMain, PING_TIMER_ID, PINGTIMER, NULL) != 0);
  1011. #endif
  1012. NOT_DATA NotData;
  1013. wcscpy(NotData.cVolumeName, VolData.cVolumeName);
  1014. Trace(log, "Received ID_CONTINUE");
  1015. VolData.EngineState = RUNNING;
  1016. //Tell the UI we've continued.
  1017. DataIoClientSetData(
  1018. ID_CONTINUE,
  1019. (PTCHAR)&NotData,
  1020. sizeof(NOT_DATA),
  1021. pdataDfrgCtl
  1022. );
  1023. break;
  1024. }
  1025. case ID_ABORT_ON_SNAPSHOT:
  1026. if (hDefragCompleteEvent){
  1027. SetEvent(hDefragCompleteEvent);
  1028. }
  1029. // fall through;
  1030. case ID_ABORT:
  1031. {
  1032. Trace(log, "Received ID_ABORT");
  1033. pDataIo = (DATA_IO*)GlobalLock((HANDLE)lParam);
  1034. if (pDataIo){
  1035. bOCXIsDead = *(BOOL *) &pDataIo->cData;
  1036. }
  1037. //0.0E00 Terminate this engine.
  1038. bTerminate = TRUE;
  1039. VolData.EngineState = TERMINATE;
  1040. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  1041. if (pDataIo) {
  1042. EH_ASSERT(GlobalUnlock((HANDLE)lParam) == FALSE);
  1043. EH_ASSERT(GlobalFree((HANDLE)lParam) == NULL);
  1044. }
  1045. break;
  1046. }
  1047. case ID_PING:
  1048. //
  1049. // Do nothing. This is just a ping sent by the UI to make sure the
  1050. // engine is still up.
  1051. //
  1052. break;
  1053. case ID_SETDISPDIMENSIONS:
  1054. {
  1055. pDataIo = (DATA_IO*)GlobalLock((HANDLE)lParam);
  1056. BOOL bSendData = TRUE;
  1057. //Make sure this is a valid size packet.
  1058. EF_ASSERT(pDataIo->ulDataSize == sizeof(SET_DISP_DATA));
  1059. SET_DISP_DATA *pSetDispData = (SET_DISP_DATA *) &pDataIo->cData;
  1060. AnalyzeView.SetNumLines(pSetDispData->AnalyzeLineCount);
  1061. if ((pSetDispData->bSendGraphicsUpdate == FALSE) &&
  1062. (AnalyzeView.IsDataSent() == TRUE)) {
  1063. bSendData = FALSE;
  1064. }
  1065. DefragView.SetNumLines(pSetDispData->DefragLineCount);
  1066. if ((pSetDispData->bSendGraphicsUpdate == FALSE) &&
  1067. (DefragView.IsDataSent() == TRUE)) {
  1068. bSendData = FALSE;
  1069. }
  1070. EH_ASSERT(GlobalUnlock((HANDLE)lParam) == FALSE);
  1071. EH_ASSERT(GlobalFree((HANDLE)lParam) == NULL);
  1072. // if the UI wants a graphics update, send data
  1073. if (bSendData) {
  1074. SendGraphicsData();
  1075. }
  1076. break;
  1077. }
  1078. default:
  1079. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1080. }
  1081. break;
  1082. case WM_TIMER:{
  1083. //
  1084. // If we're running on battery power, make sure it isn't low, critical
  1085. // or unknown
  1086. //
  1087. SYSTEM_POWER_STATUS SystemPowerStatus;
  1088. if ( GetSystemPowerStatus(&SystemPowerStatus) ){
  1089. if ((STATUS_AC_POWER_OFFLINE == SystemPowerStatus.ACLineStatus) &&
  1090. ((STATUS_BATTERY_POWER_LOW & SystemPowerStatus.BatteryFlag) ||
  1091. (STATUS_BATTERY_POWER_CRITICAL & SystemPowerStatus.BatteryFlag)
  1092. )) {
  1093. // abort all engines
  1094. TCHAR buf[256];
  1095. TCHAR buf2[256];
  1096. UINT buflen = 0;
  1097. DWORD_PTR dwParams[1];
  1098. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  1099. dwParams[0] = (DWORD_PTR) VolData.cDisplayLabel;
  1100. LoadString(GetDfrgResHandle(), IDS_APM_FAILED_ENGINE, buf, sizeof(buf) / sizeof(TCHAR));
  1101. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1102. buf, 0, 0, buf2, 256, (va_list*) dwParams)) {
  1103. break;
  1104. }
  1105. SendErrData(buf2, ENGERR_GENERAL);
  1106. }
  1107. }
  1108. if(wParam == DISKVIEW_TIMER_ID){ // graphics data
  1109. // Update the DiskView.
  1110. SendGraphicsData();
  1111. }
  1112. else if(wParam == PING_TIMER_ID && !bCommandLineUsed){
  1113. #ifndef NOTIMER
  1114. NOT_DATA NotData;
  1115. wcscpy(NotData.cVolumeName, VolData.cVolumeName);
  1116. // Kill the timer until it's been processed so we don't get a backlog of timers.
  1117. KillTimer(hwndMain, PING_TIMER_ID);
  1118. // Ping the UI.
  1119. if(!DataIoClientSetData(ID_PING, (PTCHAR)&NotData, sizeof(NOT_DATA), pdataDfrgCtl)){
  1120. //If the UI isn't there, abort.
  1121. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  1122. break;
  1123. }
  1124. // Set the timer for the next ping.
  1125. EF_ASSERT(SetTimer(hwndMain, PING_TIMER_ID, PINGTIMER, NULL) != 0);
  1126. #endif
  1127. }
  1128. break;
  1129. }
  1130. case WM_CLOSE:
  1131. {
  1132. END_SCAN_DATA EndScanData = {0};
  1133. NOT_DATA NotData;
  1134. wcscpy(EndScanData.cVolumeName, VolData.cVolumeName);
  1135. EndScanData.dwAnalyzeOrDefrag = AnalyzeOrDefrag;
  1136. if (VolData.bFragmented) {
  1137. EndScanData.dwAnalyzeOrDefrag |= DEFRAG_FAILED;
  1138. }
  1139. wcscpy(EndScanData.cFileSystem, TEXT("NTFS"));
  1140. //0.0E00 Cleanup and nuke the window.
  1141. if(bMessageWindowActive && !bCommandLineUsed){
  1142. if(!bTerminate){
  1143. //Tell the gui that the analyze and/or defrag are done.
  1144. DataIoClientSetData(
  1145. ID_END_SCAN,
  1146. (PTCHAR)&EndScanData,
  1147. sizeof(END_SCAN_DATA),
  1148. pdataDfrgCtl
  1149. );
  1150. break;
  1151. }
  1152. }
  1153. wcscpy(NotData.cVolumeName, VolData.cVolumeName);
  1154. //Tell the gui that the engine is terminating.
  1155. if (!bOCXIsDead){
  1156. DataIoClientSetData(
  1157. ID_TERMINATING,
  1158. (PTCHAR)&NotData,
  1159. sizeof(NOT_DATA),
  1160. pdataDfrgCtl
  1161. );
  1162. }
  1163. Exit();
  1164. DestroyWindow(hWnd);
  1165. break;
  1166. }
  1167. case WM_DESTROY:
  1168. //0.0E00 Nuke the thread.
  1169. PostQuitMessage(0);
  1170. break;
  1171. default:
  1172. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1173. }
  1174. return 0;
  1175. }
  1176. /*****************************************************************************************************************
  1177. ROUTINE DESCRIPTION:
  1178. This module carries out all initialization before the Analyze or Defrag threads start.
  1179. INPUT + OUTPUT:
  1180. None.
  1181. GLOBALS:
  1182. OUT hPageFileNames - Handle to the memory used to hold the names of all the pagefiles active on this drive.
  1183. OUT pPageFileNames - The pointer.
  1184. IN OUT Various VolData fields.
  1185. RETURN:
  1186. TRUE - Success.
  1187. FALSE - Fatal Error.
  1188. */
  1189. BOOL
  1190. InitializeDrive(
  1191. IN PTCHAR pCommandLine
  1192. )
  1193. {
  1194. UCHAR* pUchar = NULL;
  1195. DWORD dwComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
  1196. TCHAR cLoggerIdentifier[256];
  1197. TCHAR pParam0[100]; //NTFS or FAT
  1198. TCHAR pParam1[100]; //volume
  1199. TCHAR pParam2[100]; //Defrag or Analyze
  1200. TCHAR pParam3[100]; //UI or Command Line
  1201. TCHAR pParam4[100]; //Force Flag or Boot Optimize Flag
  1202. HKEY hValue = NULL;
  1203. TCHAR cRegValue[MAX_PATH];
  1204. DWORD dwRegValueSize = sizeof(cRegValue);
  1205. PUCHAR pMftBitmap = NULL;
  1206. PATTRIBUTE_RECORD_HEADER pArh;
  1207. VOLUME_INFORMATION* pVolInfo = NULL;
  1208. //0.0E00 Parse the command line.
  1209. pParam0[0] = 0;
  1210. pParam1[0] = 0;
  1211. pParam2[0] = 0;
  1212. pParam3[0] = 0;
  1213. pParam4[0] = 0;
  1214. swscanf(pCommandLine, TEXT("%99s %99s %99s %99s %99s"), pParam0, pParam1, pParam2, pParam3, pParam4);
  1215. //check the drive specification
  1216. // check for a x: format, sanity check on second character
  1217. if (wcslen(pParam1) == 2 && pParam1[1] == L':'){
  1218. _stprintf(VolData.cVolumeName, L"\\\\.\\%c:", pParam1[0]); // UNC format
  1219. VolData.cDrive = pParam1[0];
  1220. // Get a handle to the volume and fill in data
  1221. EF(GetNtfsVolumeStats());
  1222. // Format the VolData.DisplayLabel
  1223. FormatDisplayString(VolData.cDrive, VolData.cVolumeLabel, VolData.cDisplayLabel);
  1224. // create the tag
  1225. _stprintf(VolData.cVolumeTag, L"%c", pParam1[0]); // the drive letter only
  1226. }
  1227. // check for \\.\x:\, sanity check on third character
  1228. else if (wcslen(pParam1) == 7 && pParam1[2] == L'.'){
  1229. wcscpy(VolData.cVolumeName, pParam1); // UNC format, copy it over
  1230. VolData.cVolumeName[6] = (TCHAR) NULL; // get rid of trailing backslash
  1231. VolData.cDrive = pParam1[4];
  1232. // Get a handle to the volume and fill in data
  1233. EF(GetNtfsVolumeStats());
  1234. // Format the VolData.DisplayLabel
  1235. FormatDisplayString(VolData.cDrive, VolData.cVolumeLabel, VolData.cDisplayLabel);
  1236. // create the tag
  1237. _stprintf(VolData.cVolumeTag, L"%c", pParam1[4]); // the drive letter only
  1238. }
  1239. #ifndef VER4 // NT5 only:
  1240. // check for \\?\Volume{12a926c3-3f85-11d2-aa0e-000000000000}\,
  1241. // sanity check on third character
  1242. else if (wcslen(pParam1) == 49 && pParam1[2] == L'?'){
  1243. wcscpy(VolData.cVolumeName, pParam1); // GUID format, copy it over
  1244. VolData.cVolumeName[48] = (TCHAR) NULL; // get rid of trailing backslash
  1245. // Get a handle to the volume and fill in data
  1246. EF(GetNtfsVolumeStats());
  1247. VString mountPointList[MAX_MOUNT_POINTS];
  1248. UINT mountPointCount = 0;
  1249. // get the drive letter
  1250. if (!GetDriveLetterByGUID(VolData.cVolumeName, VolData.cDrive)){
  1251. // if we didn't get a drive letter, get the mount point list
  1252. // cause we need the list to create the DisplayLabel
  1253. GetVolumeMountPointList(
  1254. VolData.cVolumeName,
  1255. mountPointList,
  1256. mountPointCount);
  1257. }
  1258. // Format the VolData.DisplayLabel
  1259. FormatDisplayString(
  1260. VolData.cDrive,
  1261. VolData.cVolumeLabel,
  1262. mountPointList,
  1263. mountPointCount,
  1264. VolData.cDisplayLabel);
  1265. // create the tag
  1266. for (UINT i=0, j=0; i<wcslen(VolData.cVolumeName); i++){
  1267. if (iswctype(VolData.cVolumeName[i],_HEX)){
  1268. VolData.cVolumeTag[j++] = VolData.cVolumeName[i];
  1269. }
  1270. }
  1271. VolData.cVolumeTag[j] = (TCHAR) NULL;
  1272. }
  1273. #endif
  1274. else {
  1275. // invalid drive on command line
  1276. VString msg(IDS_INVALID_CMDLINE_DRIVE, GetDfrgResHandle());
  1277. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  1278. return FALSE;
  1279. }
  1280. //0.1E00 If this volume is not NTFS, error out.
  1281. if(VolData.FileSystem != FS_NTFS){
  1282. VString msg(IDMSG_ERR_NOT_NTFS_PARTITION, GetDfrgResHandle());
  1283. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  1284. return FALSE;
  1285. }
  1286. // calculate the graphics refresh interval
  1287. LONGLONG DiskSize = VolData.TotalClusters * VolData.BytesPerCluster;
  1288. LONGLONG GigSize = 1024 * 1024 * 1024;
  1289. if (DiskSize <= GigSize * 4) {
  1290. DiskViewInterval = 2000;
  1291. }
  1292. else if (DiskSize <= GigSize * 20) {
  1293. DiskViewInterval = 4000;
  1294. }
  1295. else if (DiskSize <= GigSize * 100) {
  1296. DiskViewInterval = 8000;
  1297. }
  1298. else {
  1299. DiskViewInterval = 32000;
  1300. }
  1301. //0.0E00 Get whether this is analyze or defrag from the second parameter
  1302. if(!lstrcmpi(pParam2, TEXT("ANALYZE"))){
  1303. AnalyzeOrDefrag = ANALYZE;
  1304. }
  1305. else if(!lstrcmpi(pParam2, TEXT("DEFRAG"))){
  1306. AnalyzeOrDefrag = DEFRAG;
  1307. }
  1308. else{
  1309. VString msg(IDS_INVALID_CMDLINE_OPERATION, GetDfrgResHandle());
  1310. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  1311. Trace(error, "Invalid command line specified: Aborting");
  1312. return FALSE;
  1313. }
  1314. //0.0E00 The third or fourth parameters might be set to Command Line
  1315. // which would mean this was launched from the Command Line
  1316. // I did the compare not case sensitive
  1317. if(wcslen(pParam3)){
  1318. if(_wcsicmp(TEXT("CMDLINE"), pParam3) == 0){
  1319. bCommandLineMode = TRUE;
  1320. if(wcslen(pParam4)){ //Force flag check
  1321. if(_wcsicmp(TEXT("BOOT"), pParam4) == 0){
  1322. bCommandLineBootOptimizeFlag = TRUE;
  1323. } else
  1324. {
  1325. bCommandLineBootOptimizeFlag = FALSE;
  1326. }
  1327. if(_wcsicmp(TEXT("FORCE"), pParam4) == 0){
  1328. bCommandLineForceFlag = TRUE;
  1329. } else
  1330. {
  1331. bCommandLineForceFlag = FALSE;
  1332. }
  1333. }
  1334. } else
  1335. {
  1336. bCommandLineMode = FALSE;
  1337. }
  1338. }
  1339. // open the event that was created by the UI.
  1340. // this is only used for command line operation.
  1341. // if this fails, that means there is no other process that is
  1342. // trying to sync with the engine.
  1343. if (bCommandLineMode) {
  1344. hDefragCompleteEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, DEFRAG_COMPLETE_EVENT_NAME);
  1345. if (!hDefragCompleteEvent){
  1346. Trace(warn, "Event %ws could not be opened (%lu)", DEFRAG_COMPLETE_EVENT_NAME, GetLastError());
  1347. }
  1348. }
  1349. // get the My Documents path
  1350. TCHAR cLogPath[300];
  1351. LPITEMIDLIST pidl ;
  1352. // this will get the path to My Documents for the current user
  1353. SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
  1354. SHGetPathFromIDList(pidl, cLogPath);
  1355. // initialize the log files
  1356. TCHAR cErrLogName[300];
  1357. // put error log in My Docs folder
  1358. _tcscpy(cErrLogName, cLogPath);
  1359. _tcscat(cErrLogName, TEXT("\\DfrgError.log"));
  1360. _stprintf(cLoggerIdentifier, TEXT("DfrgNtfs on Drive %s"), VolData.cDisplayLabel);
  1361. #ifdef _DEBUG
  1362. InitializeErrorLog(cErrLogName, cLoggerIdentifier);
  1363. #endif
  1364. // check registry setting for the stats log
  1365. BOOL bStatLog = FALSE;
  1366. dwRegValueSize = sizeof(cRegValue);
  1367. if (ERROR_SUCCESS == GetRegValue(
  1368. &hValue,
  1369. TEXT("SOFTWARE\\Microsoft\\Dfrg"),
  1370. TEXT("LogFilePath"),
  1371. cRegValue,
  1372. &dwRegValueSize)
  1373. ) {
  1374. RegCloseKey(hValue);
  1375. hValue = NULL;
  1376. // initialize the log which will be used to tell variation success status to dfrgtest.
  1377. if (InitializeLogFile(cRegValue)){
  1378. bLogFile = TRUE;
  1379. }
  1380. }
  1381. else {
  1382. dwRegValueSize = sizeof(cRegValue);
  1383. if (ERROR_SUCCESS == GetRegValue(
  1384. &hValue,
  1385. TEXT("SOFTWARE\\Microsoft\\Dfrg"),
  1386. TEXT("CreateLogFile"),
  1387. cRegValue,
  1388. &dwRegValueSize)
  1389. ) {
  1390. RegCloseKey(hValue);
  1391. hValue = NULL;
  1392. if(!_tcscmp(cRegValue, TEXT("1"))) {
  1393. bStatLog = TRUE;
  1394. }
  1395. }
  1396. // if we want to log statistics to a file.
  1397. if (bStatLog) {
  1398. // put error log in My Docs folder
  1399. _tcscpy(cErrLogName, cLogPath);
  1400. _tcscat(cErrLogName, TEXT("\\DfrgNTFSStats.log"));
  1401. // initialize the log which will be used to tell variation success status to dfrgtest.
  1402. if (InitializeLogFile(cErrLogName)){
  1403. bLogFile = TRUE;
  1404. }
  1405. }
  1406. }
  1407. Trace(log, "-------------------------------------------------------");
  1408. Trace(log, "Initializing Defrag engine. Commandline: %ws", pCommandLine);
  1409. Trace(log, "-------------------------------------------------------");
  1410. //0.0E00 Default to 1 frag per file
  1411. VolData.AveFragsPerFile = 100;
  1412. //0.0E00 Initialize event logging.
  1413. InitLogging(TEXT("Diskeeper"));
  1414. //Check for a dirty volume.
  1415. if (IsVolumeDirty()) {
  1416. bDirtyVolume = TRUE;
  1417. DWORD_PTR dwParams[2];
  1418. TCHAR szMsg[500];
  1419. TCHAR cString[500];
  1420. //0.1E00 IDMSG_DIRTY_VOLUME - "The de-fragmentation utility has detected that drive %s is slated to have chkdsk run: Please run chkdsk /f"
  1421. dwParams[0] = (DWORD_PTR) VolData.cDisplayLabel;
  1422. dwParams[1] = 0;
  1423. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1424. GetString(szMsg, sizeof(szMsg)/sizeof(TCHAR), IDMSG_DIRTY_VOLUME, GetDfrgResHandle()),
  1425. 0,
  1426. 0,
  1427. cString,
  1428. sizeof(cString)/sizeof(TCHAR),
  1429. (va_list*)dwParams)) {
  1430. SendErrData(NULL, ENGERR_SYSTEM);
  1431. }
  1432. else {
  1433. SendErrData(cString, ENGERR_SYSTEM);
  1434. }
  1435. // set the event to signaled, allowing the UI to proceed
  1436. if (hDefragCompleteEvent){
  1437. SetEvent(hDefragCompleteEvent);
  1438. }
  1439. //Abort the analyze/defrag.
  1440. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  1441. return TRUE;
  1442. }
  1443. //0.0E00 Allocate a buffer to hold a file record.
  1444. EF(AllocateMemory((ULONG)VolData.BytesPerFRS+(ULONG)VolData.BytesPerSector, &VolData.hFileRecord, (void**)&VolData.pFileRecord));
  1445. //0.0E00 Allocate an initial buffer to hold a file's extent list.
  1446. VolData.ExtentListAlloced = INITIAL_EXTENT_LIST_BYTES;
  1447. EF(AllocateMemory((DWORD)VolData.ExtentListAlloced, &VolData.hExtentList, (void**)&VolData.pExtentList));
  1448. //0.0E00 Allocate a buffer to hold a chunk of the MFT. We will use this in the prescan and scan.
  1449. EF(AllocateMemory((DWORD)(MFT_BUFFER_BYTES + VolData.BytesPerSector), &VolData.hMftBuffer, (void**)&VolData.pMftBuffer));
  1450. //0.0E00 Sector align the MFT buffer to speed up DASD reads.
  1451. pUchar = (PUCHAR)VolData.pMftBuffer;
  1452. //0.0E00 If any of the bits are set below the sizeof a sector (for example 512 byte sectors would be
  1453. //0x200, or binary 100000000000. Do a bitwise and with that number -1 (for example binary
  1454. //011111111111). That way, if any bits are set, this is not aligned on a sector.
  1455. if(((DWORD_PTR)VolData.pMftBuffer & (VolData.BytesPerSector-1)) != 0){
  1456. //0.0E00 pMftBuffer = pMftBuffer with all the lower bits cleared (as per the logic above) plus the number of bytes in a sector.
  1457. VolData.pMftBuffer = (PFILE_RECORD_SEGMENT_HEADER)(((DWORD_PTR)pUchar&~(VolData.BytesPerSector-1))+VolData.BytesPerSector);
  1458. }
  1459. //0.0E00 Get the MFT bitmap and count the in use file records.
  1460. EF(GetMftBitmap());
  1461. //0.0E00 Get extent list for MFT & MFT2.
  1462. EF(GetSystemsExtentList());
  1463. //0.0E00 Save the MFT extent list for DASD scans of the MFT
  1464. //Note that the MFT extent list hasn't got a stream header or anything except just the raw extents.
  1465. VolData.MftSize = VolData.FileSize;
  1466. EF(AllocateMemory((DWORD)(VolData.MftNumberOfExtents * sizeof(EXTENT_LIST)), &VolData.hMftExtentList, (void**)&VolData.pMftExtentList));
  1467. CopyMemory(VolData.pMftExtentList, VolData.pExtentList + sizeof(STREAM_EXTENT_HEADER), (ULONG)VolData.MftNumberOfExtents * sizeof(EXTENT_LIST));
  1468. //Do a version check on the NTFS volume.
  1469. //Get the $VOLUME_INFORMATION FRS.
  1470. EF((pMftBitmap = (UCHAR*)GlobalLock(VolData.hMftBitmap)) != NULL);
  1471. VolData.FileRecordNumber = 3;
  1472. if(!GetFrs(&VolData.FileRecordNumber, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord)){
  1473. GlobalUnlock(VolData.hMftBitmap);
  1474. return FALSE;
  1475. }
  1476. EF(VolData.FileRecordNumber == 3);
  1477. // This gets a pointer to the correct attribute as pArh
  1478. EF(FindAttributeByType($VOLUME_INFORMATION, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, &pArh, (ULONG)VolData.BytesPerFRS));
  1479. GlobalUnlock(VolData.hMftBitmap);
  1480. //0.0E00 Get pointer to the version number structure -- THIS FRS MUST BE RESIDENT PER THE FILE SYSTEM SPECIFICATION.
  1481. pVolInfo = (VOLUME_INFORMATION*)((DWORD_PTR)pArh+pArh->Form.Resident.ValueOffset);
  1482. //Check to make sure that this is NTFS major version 1 or 2. 3 is NTFS 5.0 which we don't support.
  1483. if((pVolInfo->MajorVersion > 3) || (pVolInfo->MajorVersion < 1)){
  1484. //IDS_UNSUPPORTED_NTFS_VERSION - The NTFS volume you are attempting to defrag is not a version supported by DfrgNtfs. DfrgNtfs only supports versions of NTFS created by Windows NT 3.1 through Windows NT 5.0.
  1485. VString msg(IDS_UNSUPPORTED_NTFS_VERSION, GetDfrgResHandle());
  1486. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  1487. return FALSE;
  1488. }
  1489. //0.0E00 Get this computer's name.
  1490. EF(GetComputerName((LPTSTR)VolData.NodeName, &dwComputerNameSize));
  1491. //0.0E00 Get the pagefile names.
  1492. // LEAVE THE DRIVE LETTER HERE - PAGEFILES CANNOT BE PUT ON A MOUNTED VOLUME
  1493. EF(GetPagefileNames(VolData.cDrive, &hPageFileNames, &pPageFileNames));
  1494. //1.0E00 Allocate buffer to hold the volume bitmap - don't lock
  1495. //Header plus the number of bytes to fit the bitmap, plus one byte in case it's not an even division.
  1496. EF_ASSERT(VolData.BitmapSize);
  1497. EF(AllocateMemory((DWORD)(sizeof(VOLUME_BITMAP_BUFFER) + (VolData.BitmapSize / 8) + 1 + VolData.BytesPerSector),
  1498. &VolData.hVolumeBitmap,
  1499. NULL));
  1500. //1.0E00 Load the volume bitmap.
  1501. EF(GetVolumeBitmap());
  1502. //0.0E00 Set the timer for updating the DiskView.
  1503. EF_ASSERT(SetTimer(hwndMain, DISKVIEW_TIMER_ID, DiskViewInterval, NULL) != 0);
  1504. //0.0E00 Set the timer that will ping the UI.
  1505. // DO NOT set this timer is this is the command line version 'cause the engine will kill itself
  1506. #ifndef NOTIMER
  1507. if (!bCommandLineMode){
  1508. EF(SetTimer(hwndMain, PING_TIMER_ID, PINGTIMER, NULL) != 0);
  1509. }
  1510. #endif
  1511. //Ok don't terminate before closing the display window.
  1512. bTerminate = FALSE;
  1513. //Set the engine state to running.
  1514. VolData.EngineState = RUNNING;
  1515. //Send a message to the UI telling it that the process has started and what type of pass this is.
  1516. ENGINE_START_DATA EngineStartData = {0};
  1517. wcscpy(EngineStartData.cVolumeName, VolData.cVolumeName);
  1518. EngineStartData.dwAnalyzeOrDefrag = AnalyzeOrDefrag;
  1519. wcscpy(EngineStartData.cFileSystem, TEXT("NTFS"));
  1520. DataIoClientSetData(ID_ENGINE_START, (PTCHAR)&EngineStartData, sizeof(ENGINE_START_DATA), pdataDfrgCtl);
  1521. Trace(log, "Successfully finished initializing drive %ws", VolData.cDisplayLabel);
  1522. //0.0E00 After Initialize, determine whether this is an analyze or defrag run, and start the approriate one.
  1523. switch(AnalyzeOrDefrag){
  1524. case ANALYZE:
  1525. PostMessage(hwndMain, WM_COMMAND, ID_ANALYZE, 0);
  1526. break;
  1527. case DEFRAG:
  1528. PostMessage(hwndMain, WM_COMMAND, ID_DEFRAG, 0);
  1529. break;
  1530. default:
  1531. EF(FALSE);
  1532. }
  1533. return TRUE;
  1534. }
  1535. /*******************************************************************************
  1536. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1537. ROUTINE DESCRIPTION:
  1538. This module carries out initialization specific to defrag before the defrag thread starts.
  1539. INPUT + OUTPUT:
  1540. None.
  1541. GLOBALS:
  1542. OUT bMoveDirs - TRUE if the registry says to move directories.
  1543. IN OUT Various VolData fields.
  1544. RETURN:
  1545. TRUE - Success.
  1546. FALSE - Fatal Error.
  1547. */
  1548. BOOL
  1549. InitializeDefrag(
  1550. )
  1551. {
  1552. TCHAR cExcludeFile[100];
  1553. BEGIN_SCAN_INFO ScanInfo = {0};
  1554. //1.0E00 Get the exclude list if any.
  1555. // what is this stuff?!
  1556. _stprintf(cExcludeFile, TEXT("Exclude%s.dat"), VolData.cVolumeTag);
  1557. GetExcludeFile(cExcludeFile, &VolData.hExcludeList);
  1558. // Copy the analyze cluster array (DiskView class)
  1559. DefragView = AnalyzeView;
  1560. SendGraphicsData();
  1561. wcscpy(ScanInfo.cVolumeName, VolData.cVolumeName);
  1562. wcscpy(ScanInfo.cFileSystem, TEXT("NTFS"));
  1563. //The defrag fields will equal zero since the structure is zero memoried above. This means we're not sending defrag data.
  1564. // Tell the UI that we're beginning the scan.
  1565. DataIoClientSetData(ID_BEGIN_SCAN, (PTCHAR)&ScanInfo, sizeof(BEGIN_SCAN_INFO), pdataDfrgCtl);
  1566. return TRUE;
  1567. }
  1568. /*******************************************************************************
  1569. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1570. ROUTINE DESCRIPTION:
  1571. Do the scan of the volume, filling in the file lists with the extent data for each file on the volume.
  1572. INPUT + OUTPUT:
  1573. None.
  1574. GLOBALS:
  1575. IN OUT Various VolData fields.
  1576. RETURN:
  1577. TRUE - Success.
  1578. FALSE - Fatal Error.
  1579. */
  1580. BOOL
  1581. ScanNtfs(
  1582. IN CONST BOOL fAnalyseOnly
  1583. )
  1584. {
  1585. UCHAR* pMftBitmap = NULL;
  1586. UCHAR* pUchar = NULL;
  1587. BOOL bResult = FALSE;
  1588. BOOL bBootOptimise = FALSE;
  1589. UINT uPercentDonePrevious = 0;
  1590. ULONG NumFiles = 0;
  1591. ULONG NumDirs = 0;
  1592. ULONG NumMoveableFiles = 0;
  1593. LONGLONG TotalFileRecords = VolData.TotalFileRecords;
  1594. TCHAR cName[MAX_PATH+1];
  1595. LONGLONG ParentFileRecordNumber;
  1596. BEGIN_SCAN_INFO ScanInfo = {0};
  1597. FILE_RECORD_SEGMENT_HEADER* pFileRecord =
  1598. (FILE_RECORD_SEGMENT_HEADER*) VolData.pFileRecord;
  1599. FILE_EXTENT_HEADER* pFileExtentHeader =
  1600. (FILE_EXTENT_HEADER*) VolData.pExtentList;
  1601. Trace(log, "Start: NTFS File Scan");
  1602. VolData.bMFTCorrupt = FALSE;
  1603. //0.0E00 Get a pointer to the MFT bitmap
  1604. pMftBitmap = (UCHAR*)GlobalLock(VolData.hMftBitmap);
  1605. if (NULL == pMftBitmap) {
  1606. LOG_ERR();
  1607. Trace(log, "End: NTFS File Scan. Error encountered while getting "
  1608. "volume MFT bitmap");
  1609. assert(FALSE);
  1610. return FALSE;
  1611. }
  1612. __try {
  1613. // Create a DiskView class cluster array for this volume
  1614. AnalyzeView.SetClusterCount((int)VolData.TotalClusters);
  1615. AnalyzeView.SetMftZone(
  1616. (int)VolData.MftZoneStart,
  1617. (int)VolData.MftZoneEnd
  1618. );
  1619. // Create a buffer to hold extent updates for DiskView.
  1620. bResult = CreateExtentBuffer();
  1621. if (!bResult) {
  1622. LOG_ERR();
  1623. Trace(log, "End: NTFS File Scan. Error encountered while creating "
  1624. "volume extent buffer");
  1625. return FALSE;
  1626. }
  1627. //
  1628. // The defrag fields will equal zero since the structure is zero
  1629. // memoried above. This means we're not sending defrag data.
  1630. // Tell the UI that we're beginning the scan.
  1631. wcscpy(ScanInfo.cVolumeName, VolData.cVolumeName);
  1632. wcscpy(ScanInfo.cFileSystem, TEXT("NTFS"));
  1633. DataIoClientSetData(ID_BEGIN_SCAN,
  1634. (PTCHAR)&ScanInfo,
  1635. sizeof(BEGIN_SCAN_INFO),
  1636. pdataDfrgCtl);
  1637. if ((!fAnalyseOnly) && IsBootVolume(VolData.cDrive)) {
  1638. bBootOptimise = TRUE;
  1639. }
  1640. if (bBootOptimise) {
  1641. InitialiseBootOptimise(TRUE);
  1642. }
  1643. // Get extent list for MFT & MFT2.
  1644. EF(GetSystemsExtentList());
  1645. //
  1646. // Add the MFT extents to the diskview. If the MFT is greater than
  1647. // 2 extents, make it red, else make it blue.
  1648. //
  1649. if (VolData.MftNumberOfExtents > 2) {
  1650. bResult = AddExtentsStream(FragmentColor,
  1651. (STREAM_EXTENT_HEADER*)VolData.pExtentList);
  1652. }
  1653. else {
  1654. bResult = AddExtentsStream(UsedSpaceColor,
  1655. (STREAM_EXTENT_HEADER*)VolData.pExtentList);
  1656. }
  1657. if (!bResult) {
  1658. LOG_ERR();
  1659. Trace(log, "End: NTFS File Scan. Error encountered while adding "
  1660. "file extents for MFT");
  1661. return FALSE;
  1662. }
  1663. //
  1664. // Get the root dir's base file record
  1665. //
  1666. VolData.FileRecordNumber = ROOT_FILE_NAME_INDEX_NUMBER;
  1667. bResult = GetInUseFrs(VolData.hVolume,
  1668. &VolData.FileRecordNumber,
  1669. pFileRecord,
  1670. (ULONG)VolData.BytesPerFRS
  1671. );
  1672. if ((!bResult) ||
  1673. (ROOT_FILE_NAME_INDEX_NUMBER != VolData.FileRecordNumber)
  1674. ) {
  1675. LOG_ERR();
  1676. Trace(log, "End: NTFS File Scan. Error encountered while getting "
  1677. "file record for root directory");
  1678. return FALSE;
  1679. }
  1680. // Count the root directory as one of the directories.
  1681. NumDirs++;
  1682. // Check to see if it's a nonresident dir.
  1683. if (IsNonresidentFile(DEFAULT_STREAMS, pFileRecord)) {
  1684. // Get the root dir's extent list.
  1685. bResult = GetExtentList(DEFAULT_STREAMS, pFileRecord);
  1686. // Now add the root dir to the disk view map of disk clusters.
  1687. if (bResult) {
  1688. bResult = AddExtents((TRUE == VolData.bFragmented) ?
  1689. FragmentColor : UsedSpaceColor);
  1690. }
  1691. if (!bResult) {
  1692. LOG_ERR();
  1693. Trace(log, "End: NTFS File Scan. Error encountered while "
  1694. "processing root directory");
  1695. return FALSE;
  1696. }
  1697. if (bBootOptimise) {
  1698. if (!UpdateInBootOptimiseList() && (VolData.bInBootExcludeZone)) {
  1699. AddFileToListNtfs(&VolData.FilesInBootExcludeZoneTable,
  1700. VolData.FileRecordNumber);
  1701. }
  1702. }
  1703. // If it is fragmented update the appropriate statistics.
  1704. if (TRUE == VolData.bFragmented) {
  1705. bResult = AddFileToListNtfs(
  1706. &VolData.FragmentedFileTable,
  1707. VolData.FileRecordNumber
  1708. );
  1709. // Keep track of fragged statstics.
  1710. VolData.FraggedSpace +=
  1711. VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  1712. VolData.NumFraggedDirs ++;
  1713. VolData.NumExcessDirFrags += VolData.NumberOfFragments - 1;
  1714. VolData.InitialFraggedClusters += VolData.NumberOfRealClusters;
  1715. }
  1716. else {
  1717. if (!fAnalyseOnly) {
  1718. bResult = AddFileToListNtfs(
  1719. &VolData.ContiguousFileTable,
  1720. VolData.FileRecordNumber
  1721. );
  1722. }
  1723. VolData.InitialContiguousClusters += VolData.NumberOfRealClusters;
  1724. }
  1725. }
  1726. // Scan the MFT for fragmented files, directories & pagefile.
  1727. for (VolData.FileRecordNumber = FIRST_USER_FILE_NUMBER;
  1728. VolData.FileRecordNumber < TotalFileRecords;
  1729. VolData.FileRecordNumber ++){
  1730. // Sleep if paused.
  1731. while (PAUSED == VolData.EngineState) {
  1732. Sleep(1000);
  1733. }
  1734. // Exit if the controller wants us to stop.
  1735. if (TERMINATE == VolData.EngineState) {
  1736. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  1737. ExitThread(0);
  1738. }
  1739. // Calculate the percentage of analysis
  1740. uPercentDone = (int)(((double) VolData.FileRecordNumber /
  1741. (double) TotalFileRecords) * 100);
  1742. if ((uPercentDone < 1) &&
  1743. (uPercentDone != uPercentDonePrevious)) {
  1744. uPercentDone = 1;
  1745. SendStatusData();
  1746. }
  1747. if (((uPercentDone % 5) == 0) &&
  1748. (uPercentDone != uPercentDonePrevious) &&
  1749. (uPercentDone > 0)
  1750. ) {
  1751. SendStatusData();
  1752. uPercentDonePrevious = uPercentDone;
  1753. }
  1754. // Load next in-use file record.
  1755. bResult = GetFrs(
  1756. &VolData.FileRecordNumber,
  1757. VolData.pMftExtentList,
  1758. pMftBitmap,
  1759. VolData.pMftBuffer,
  1760. pFileRecord
  1761. );
  1762. if (!bResult) {
  1763. if (VolData.bMFTCorrupt) {
  1764. Trace(log, "End: NTFS File Scan. MFT appears to be "
  1765. "corrupt");
  1766. return FALSE;
  1767. }
  1768. continue;
  1769. }
  1770. // Skip if we're past the end of the MFT.
  1771. if (VolData.FileRecordNumber >= TotalFileRecords) {
  1772. continue;
  1773. }
  1774. // Skip if this is a secondary file record.
  1775. if (pFileRecord->ReferenceCount == 0) {
  1776. continue;
  1777. }
  1778. // Check if this is a directory or a file.
  1779. if (pFileRecord->Flags & FILE_FILE_NAME_INDEX_PRESENT) {
  1780. // Count that we found a dir.
  1781. NumDirs++;
  1782. }
  1783. else {
  1784. // Count that we found a file.
  1785. NumFiles++;
  1786. }
  1787. // Skip this file record if it has nothing to move.
  1788. if (!IsNonresidentFile(DEFAULT_STREAMS, pFileRecord)) {
  1789. continue;
  1790. }
  1791. // This, of course, also includes moveable directories.
  1792. NumMoveableFiles++;
  1793. // Get the file's extent list
  1794. if (!GetExtentList(DEFAULT_STREAMS, pFileRecord)) {
  1795. continue;
  1796. }
  1797. if ((VolData.NumberOfFragments < 1) ||
  1798. (VolData.NumberOfClusters < 1)) {
  1799. continue;
  1800. }
  1801. if (bBootOptimise) {
  1802. if (!UpdateInBootOptimiseList() && (VolData.bInBootExcludeZone)) {
  1803. AddFileToListNtfs(&VolData.FilesInBootExcludeZoneTable,
  1804. VolData.FileRecordNumber);
  1805. }
  1806. }
  1807. // If this is a directory...
  1808. if (VolData.bDirectory) {
  1809. // Add the file to the appropriate list, and update the statistics.
  1810. if(VolData.bFragmented == TRUE){
  1811. bResult = AddFileToListNtfs(
  1812. &VolData.FragmentedFileTable,
  1813. VolData.FileRecordNumber
  1814. );
  1815. if (bResult) {
  1816. bResult = AddExtents(FragmentColor);
  1817. }
  1818. if (!bResult) {
  1819. LOG_ERR();
  1820. Trace(log, "End: NTFS File Scan, 1. Error encountered "
  1821. "while processing directories");
  1822. return FALSE;
  1823. }
  1824. // Keep track of fragged statstics.
  1825. VolData.FraggedSpace +=
  1826. VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  1827. VolData.NumFraggedDirs++;
  1828. VolData.NumExcessDirFrags += VolData.NumberOfFragments - 1;
  1829. VolData.InitialFraggedClusters += VolData.NumberOfRealClusters;
  1830. }
  1831. else {
  1832. if (!fAnalyseOnly) {
  1833. bResult = AddFileToListNtfs(
  1834. &VolData.ContiguousFileTable,
  1835. VolData.FileRecordNumber
  1836. );
  1837. }
  1838. if (bResult) {
  1839. bResult = AddExtents(UsedSpaceColor);
  1840. }
  1841. if (!bResult) {
  1842. LOG_ERR();
  1843. Trace(log, "End: NTFS File Scan, 2. Error encountered "
  1844. "while processing directories");
  1845. return FALSE;
  1846. }
  1847. VolData.InitialContiguousClusters += VolData.NumberOfRealClusters;
  1848. }
  1849. }
  1850. else {
  1851. // Process files
  1852. // Keep track of the total number of files so far.
  1853. VolData.CurrentFile++;
  1854. // Keep track of how many bytes there are in all files we've processed.
  1855. VolData.TotalFileSpace
  1856. += VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  1857. VolData.TotalFileBytes += VolData.FileSize;
  1858. //
  1859. // Check to see if it's a pagefile, and if so, record the
  1860. // sizeof the pagefile extents.
  1861. //
  1862. bResult = CheckForPagefileNtfs(
  1863. VolData.FileRecordNumber,
  1864. pFileRecord);
  1865. if (!bResult) {
  1866. LOG_ERR();
  1867. continue;
  1868. }
  1869. //
  1870. // VolData.pPageFile is set TRUE/FALSE from above. If it is
  1871. // a pagefile, add it to the pagefile list, and put its extents
  1872. // into DiskView.
  1873. //
  1874. if (!CheckFileForExclude(TRUE) || (VolData.bPageFile)) {
  1875. VolData.bPageFile = FALSE;
  1876. // Add page files to the page file list.
  1877. if (!fAnalyseOnly) {
  1878. bResult = AddFileToListNtfs(
  1879. &VolData.NonMovableFileTable,
  1880. VolData.FileRecordNumber
  1881. );
  1882. }
  1883. if (bResult) {
  1884. // Now add the file to the disk view map
  1885. bResult = AddExtents(PageFileColor);
  1886. }
  1887. if (!bResult) {
  1888. LOG_ERR();
  1889. Trace(log, "End: NTFS File Scan, 1. Error encountered "
  1890. "while processing pagefiles");
  1891. return FALSE;
  1892. }
  1893. // Keep track of fragged statistics.
  1894. if (VolData.bFragmented){
  1895. VolData.FraggedSpace += VolData.NumberOfRealClusters *
  1896. VolData.BytesPerCluster;
  1897. VolData.NumFraggedFiles++;
  1898. VolData.NumExcessFrags += VolData.NumberOfFragments - 1;
  1899. VolData.InitialFraggedClusters += VolData.NumberOfRealClusters;
  1900. }
  1901. else {
  1902. VolData.InitialContiguousClusters += VolData.NumberOfRealClusters;
  1903. }
  1904. }
  1905. else {
  1906. // This is not a pagefile
  1907. // Add moveable files to the moveable file list.
  1908. if (VolData.bFragmented){
  1909. bResult = AddFileToListNtfs(
  1910. &VolData.FragmentedFileTable,
  1911. VolData.FileRecordNumber
  1912. );
  1913. if (bResult) {
  1914. //Now add the file to the disk view
  1915. bResult = AddExtents(FragmentColor);
  1916. }
  1917. if (!bResult) {
  1918. LOG_ERR();
  1919. Trace(log, "End: NTFS File Scan, 2. Error encountered "
  1920. "while processing files");
  1921. return FALSE;
  1922. }
  1923. //0.0E00 Keep track of fragged statistics.
  1924. VolData.FraggedSpace += VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  1925. VolData.NumFraggedFiles++;
  1926. VolData.NumExcessFrags += VolData.NumberOfFragments - 1;
  1927. VolData.InitialFraggedClusters += VolData.NumberOfRealClusters;
  1928. }
  1929. else{ // not fragmented
  1930. if (!fAnalyseOnly) {
  1931. bResult = AddFileToListNtfs(
  1932. &VolData.ContiguousFileTable,
  1933. VolData.FileRecordNumber
  1934. );
  1935. }
  1936. if (bResult) {
  1937. // Now add the file to the disk view
  1938. bResult = AddExtents(UsedSpaceColor);
  1939. }
  1940. if (!bResult) {
  1941. LOG_ERR();
  1942. Trace(log, "End: NTFS File Scan, 3. Error encountered "
  1943. "while processing files");
  1944. return FALSE;
  1945. }
  1946. VolData.InitialContiguousClusters += VolData.NumberOfRealClusters;
  1947. }
  1948. }
  1949. }
  1950. // update cluster array
  1951. PurgeExtentBuffer();
  1952. }
  1953. //Note the total number of files on the disk for statistics purposes.
  1954. VolData.TotalFiles = NumFiles;
  1955. //Note the total number of dirs on the disk for statistics purposes.
  1956. VolData.TotalDirs = NumDirs;
  1957. //0.0E00 Keep track of the average file size.
  1958. if(VolData.CurrentFile != 0){
  1959. VolData.AveFileSize = VolData.TotalFileBytes / VolData.CurrentFile;
  1960. }
  1961. // Validate data and keep track of the percent of the disk that is fragmented.
  1962. if (VolData.UsedSpace != 0) {
  1963. VolData.PercentDiskFragged = 100 * VolData.FraggedSpace / VolData.UsedSpace;
  1964. }
  1965. else if (VolData.UsedClusters != 0 && VolData.BytesPerCluster != 0) {
  1966. VolData.PercentDiskFragged = (100 * VolData.FraggedSpace) /
  1967. (VolData.UsedClusters * VolData.BytesPerCluster);
  1968. }
  1969. // Validate data and keep track of the average number of fragments per file.
  1970. if((VolData.NumFraggedFiles != 0) && (VolData.CurrentFile != 0)) {
  1971. VolData.AveFragsPerFile = ((VolData.NumExcessFrags + VolData.CurrentFile) * 100) / VolData.CurrentFile;
  1972. }
  1973. //Send status data to the UI.
  1974. SendStatusData();
  1975. //Send graphical data to the UI.
  1976. SendGraphicsData();
  1977. }
  1978. __finally {
  1979. //0.0E00 Free up the MFT bitmap.
  1980. if(VolData.hMftBitmap != NULL){
  1981. EH_ASSERT(GlobalUnlock(VolData.hMftBitmap) == FALSE);
  1982. EH_ASSERT(GlobalFree(VolData.hMftBitmap) == NULL);
  1983. VolData.hMftBitmap = NULL;
  1984. }
  1985. }
  1986. Trace(log, "End: NTFS File Scan. %lu Fragmented files, %lu Contiguous Files.",
  1987. RtlNumberGenericTableElementsAvl(&VolData.FragmentedFileTable),
  1988. RtlNumberGenericTableElementsAvl(&VolData.ContiguousFileTable)
  1989. );
  1990. return TRUE;
  1991. }
  1992. /*******************************************************************************
  1993. ROUTINE DESCRIPTION:
  1994. Thread routine for analysis.
  1995. INPUT + OUTPUT:
  1996. None.
  1997. GLOBALS:
  1998. IN hwndMain - Handle to the main window.
  1999. NOTE:
  2000. When aborting, the main thread doesn't wait for this thread to
  2001. clean-up. Therefore, DO NOT have state (temp files, global events, etc)
  2002. that need to be cleaned up anywhere in this thread.
  2003. RETURN:
  2004. TRUE - Success.
  2005. FALSE - Fatal Error.
  2006. */
  2007. BOOL
  2008. AnalyzeThread(
  2009. )
  2010. {
  2011. BOOL bResult = FALSE;
  2012. Trace(log, "Start: Volume Analysis for %ws", VolData.cDisplayLabel);
  2013. CoInitializeEx(NULL, COINIT_MULTITHREADED);
  2014. // Statistical info. Get the time that the engine started.
  2015. GetLocalTime(&VolData.StartTime);
  2016. uPercentDone = 0;
  2017. uEngineState = DEFRAG_STATE_ANALYZING;
  2018. SendStatusData();
  2019. //
  2020. // There may be files on the volume with ACLs such that even an
  2021. // Administrator cannot open them. Acquire the backup privilege: this
  2022. // lets us bypass the ACL checks when opening files.
  2023. //
  2024. AcquirePrivilege(SE_BACKUP_NAME);
  2025. // Exit if the controller wants us to stop.
  2026. if (TERMINATE == VolData.EngineState) {
  2027. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2028. ExitThread(0);
  2029. }
  2030. //
  2031. // Initialise the file list tables. Note that since we're only analysing,
  2032. // we can pas in TRUE for fAnalyseOnly, which takes shortcuts to make this
  2033. // faster. If this fails, we can't go any further since we need the tables.
  2034. //
  2035. bResult = AllocateFileLists(TRUE);
  2036. if (!bResult) {
  2037. LOG_ERR();
  2038. Trace(error, "End: Volume Analysis. Errors encountered while "
  2039. "allocating internal data structures.");
  2040. return FALSE;
  2041. }
  2042. //
  2043. // Scan the MFT and build up the tables of interest. If this fails, there
  2044. // isn't much we can do other than abort.
  2045. //
  2046. bResult = ScanNtfs(TRUE);
  2047. if (!bResult) {
  2048. //
  2049. // If we detected any MFT corruption, put up a message asking the user
  2050. // to run chkdsk. Otherwise, just put up a generic failure message
  2051. // with the last file-name we were scanning when we failed.
  2052. //
  2053. if (VolData.bMFTCorrupt) {
  2054. DWORD_PTR dwParams[3];
  2055. TCHAR szMsg[500];
  2056. TCHAR cString[500];
  2057. Trace(error, "End: Volume Analysis. Errors encountered while "
  2058. "scanning the volume (MFT appears to be corrupt)");
  2059. //
  2060. // IDS_CORRUPT_MFT - "Defragmentation of %1!s! has been aborted due
  2061. // to inconsistencies that were detected in the filesystem. Please
  2062. // run CHKDSK or SCANDISK on %2!s! to repair these inconsistencies,
  2063. // then run Disk Defragmenter again"
  2064. //
  2065. dwParams[0] = (DWORD_PTR) VolData.cDisplayLabel;
  2066. dwParams[1] = (DWORD_PTR) VolData.cDisplayLabel;
  2067. dwParams[2] = 0;
  2068. bResult = FormatMessage(
  2069. FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  2070. GetString(szMsg, sizeof(szMsg)/sizeof(TCHAR),
  2071. IDS_CORRUPT_MFT, GetDfrgResHandle()),
  2072. 0,
  2073. 0,
  2074. cString,
  2075. sizeof(cString)/sizeof(TCHAR),
  2076. (va_list*)dwParams);
  2077. if (!bResult) {
  2078. SendErrData(NULL, ENGERR_CORRUPT_MFT);
  2079. }
  2080. else {
  2081. SendErrData(cString, ENGERR_CORRUPT_MFT);
  2082. }
  2083. }
  2084. else {
  2085. PWSTR pszTemp = NULL;
  2086. //
  2087. // IDMSG_SCANNTFS_SCAN_ABORT - "ScanNTFS: Scan Aborted - Fatal
  2088. // Error - File:"
  2089. //
  2090. VString msg(IDMSG_SCANNTFS_SCAN_ABORT, GetDfrgResHandle());
  2091. msg.AddChar(L' ');
  2092. //
  2093. // Get the name of the last file we were scannning. If it begins
  2094. // with the Volume GUID (is of the form \??\Volume{GUID}\file.txt),
  2095. // strip off the volume GUID (and put in the drive letter if
  2096. // possible).
  2097. //
  2098. GetNtfsFilePath();
  2099. pszTemp = VolData.vFileName.GetBuffer();
  2100. if (StartsWithVolumeGuid(pszTemp)) {
  2101. if ((VolData.cDrive >= L'C') && (VolData.cDrive <= L'Z')) {
  2102. // The drive letter is valid.
  2103. msg.AddChar(VolData.cDrive);
  2104. msg.AddChar(L':');
  2105. }
  2106. msg += (PWSTR)(pszTemp + 48);
  2107. }
  2108. else {
  2109. if (VolData.vFileName.GetBuffer()) {
  2110. msg += VolData.vFileName;
  2111. }
  2112. }
  2113. Trace(error, "End: Volume Analysis. Errors encountered "
  2114. "while scanning the volume (last file processed: %ws)",
  2115. VolData.vFileName.GetBuffer());
  2116. // Send error info to client
  2117. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  2118. }
  2119. // Trigger an abort.
  2120. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  2121. // Set the event to signaled, allowing the UI to proceed
  2122. if (hDefragCompleteEvent){
  2123. SetEvent(hDefragCompleteEvent);
  2124. }
  2125. // There isn't much else to do
  2126. ExitThread(0);
  2127. return TRUE;
  2128. }
  2129. // Statistical info: Note the end time for that pass.
  2130. GetLocalTime(&VolData.EndTime);
  2131. // Log the basic statistics for this if needed.
  2132. DisplayNtfsVolumeStats();
  2133. // Send stuff (status, bitmap, report) to the UI
  2134. uEngineState = DEFRAG_STATE_ANALYZED;
  2135. uPercentDone = 100;
  2136. SendStatusData();
  2137. SendGraphicsData();
  2138. SendReportData();
  2139. // Send the most fragged list to the UI.
  2140. SendMostFraggedList(TRUE);
  2141. //
  2142. // Now clean-up the extent buffer. This will purge it as well, so we'll
  2143. // have a fully up-to-date DiskView of the disk.
  2144. //
  2145. // (ignoring return value)
  2146. DestroyExtentBuffer();
  2147. // We're done, close down now.
  2148. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2149. // Set the event to signaled, allowing the UI to proceed
  2150. if (hDefragCompleteEvent){
  2151. SetEvent(hDefragCompleteEvent);
  2152. }
  2153. Trace(log, "End: Volume Analysis for %ws (completed successfully)",
  2154. VolData.cDisplayLabel);
  2155. //0.0E00 Kill the thread.
  2156. ExitThread(0);
  2157. return TRUE;
  2158. }
  2159. /*******************************************************************************
  2160. ROUTINE DESCRIPTION:
  2161. Thread routine for defragmentation.
  2162. INPUT + OUTPUT:
  2163. None.
  2164. GLOBALS:
  2165. IN hwndMain - Handle to the main window.
  2166. NOTE:
  2167. When aborting, the main thread doesn't wait for this thread to
  2168. clean-up. Therefore, DO NOT have state (temp files, global events, etc)
  2169. that need to be cleaned up anywhere in this thread.
  2170. RETURN:
  2171. TRUE - Success.
  2172. FALSE - Fatal Error.
  2173. */
  2174. BOOL
  2175. DefragThread(
  2176. )
  2177. {
  2178. BOOL bResult = FALSE;
  2179. DWORD dwLayoutErrorCode = ENG_NOERR;
  2180. Trace(log, "Start: Volume Defragmentation for %ws", VolData.cDisplayLabel);
  2181. CoInitializeEx(NULL, COINIT_MULTITHREADED);
  2182. // Statistical info. Get the time that the engine started.
  2183. GetLocalTime(&VolData.StartTime);
  2184. uEngineState = DEFRAG_STATE_REANALYZING;
  2185. SendStatusData();
  2186. //
  2187. // There may be files on the volume with ACLs such that even an
  2188. // Administrator cannot open them. Acquire the backup privilege: this
  2189. // lets us bypass the ACL checks when opening files.
  2190. //
  2191. AcquirePrivilege(SE_BACKUP_NAME);
  2192. // Exit if the controller (UI/command-line) wants us to stop.
  2193. if (TERMINATE == VolData.EngineState) {
  2194. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2195. // Set the event to signaled, allowing the UI to proceed
  2196. if (hDefragCompleteEvent){
  2197. SetEvent(hDefragCompleteEvent);
  2198. }
  2199. ExitThread(0);
  2200. }
  2201. //
  2202. // Initialise the file list tables. Note that since we're defragmenting,
  2203. // we're passing in FALSE for fAnalyseOnly. This will allocate all the
  2204. // tables we care about (instead of taking the shortcut that Analyse does).
  2205. //
  2206. // If this fails, we can't go any further since we need the tables.
  2207. //
  2208. bResult = AllocateFileLists(FALSE);
  2209. if (!bResult) {
  2210. LOG_ERR();
  2211. Trace(error, "End: Volume Defragmentation. Errors encountered while "
  2212. "allocating internal data structures.");
  2213. // Set the event to signaled, allowing the UI to proceed
  2214. if (hDefragCompleteEvent){
  2215. SetEvent(hDefragCompleteEvent);
  2216. }
  2217. return FALSE;
  2218. }
  2219. //
  2220. // Scan the MFT and build up the tables of interest. If this fails, there
  2221. // isn't much we can do other than abort.
  2222. //
  2223. bResult = ScanNtfs(FALSE);
  2224. if (!bResult) {
  2225. //
  2226. // If we detected any MFT corruption, put up a message asking the user
  2227. // to run chkdsk. Otherwise, just put up a generic failure message
  2228. // with the last file-name we were scanning when we failed.
  2229. //
  2230. if (VolData.bMFTCorrupt) {
  2231. Trace(error, "End: Volume Defragmentation. Errors encountered "
  2232. "while scanning the volume (MFT appears to be "
  2233. "corrupt)");
  2234. DWORD_PTR dwParams[3];
  2235. TCHAR szMsg[500];
  2236. TCHAR cString[500];
  2237. //
  2238. // IDS_CORRUPT_MFT - "Defragmentation of %1!s! has been aborted due
  2239. // to inconsistencies that were detected in the filesystem. Please
  2240. // run CHKDSK or SCANDISK on %2!s! to repair these inconsistencies,
  2241. // then run Disk Defragmenter again"
  2242. //
  2243. dwParams[0] = (DWORD_PTR) VolData.cDisplayLabel;
  2244. dwParams[1] = (DWORD_PTR) VolData.cDisplayLabel;
  2245. dwParams[2] = 0;
  2246. bResult = FormatMessage(
  2247. FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  2248. GetString(szMsg, sizeof(szMsg)/sizeof(TCHAR),
  2249. IDS_CORRUPT_MFT, GetDfrgResHandle()),
  2250. 0,
  2251. 0,
  2252. cString,
  2253. sizeof(cString)/sizeof(TCHAR),
  2254. (va_list*)dwParams);
  2255. if (!bResult) {
  2256. SendErrData(NULL, ENGERR_CORRUPT_MFT);
  2257. }
  2258. else {
  2259. SendErrData(cString, ENGERR_CORRUPT_MFT);
  2260. }
  2261. }
  2262. else {
  2263. PWSTR pszTemp = NULL;
  2264. //
  2265. // IDMSG_SCANNTFS_SCAN_ABORT - "ScanNTFS: Scan Aborted - Fatal
  2266. // Error - File:"
  2267. //
  2268. VString msg(IDMSG_SCANNTFS_SCAN_ABORT, GetDfrgResHandle());
  2269. msg.AddChar(L' ');
  2270. //
  2271. // Get the name of the last file we were scannning. If it begins
  2272. // with the Volume GUID (is of the form \??\Volume{GUID}\file.txt),
  2273. // strip off the volume GUID (and put in the drive letter if
  2274. // possible).
  2275. //
  2276. GetNtfsFilePath();
  2277. pszTemp = VolData.vFileName.GetBuffer();
  2278. if (StartsWithVolumeGuid(pszTemp)) {
  2279. if ((VolData.cDrive >= L'C') && (VolData.cDrive <= L'Z')) {
  2280. //
  2281. // The drive letter is valid.
  2282. //
  2283. msg.AddChar(VolData.cDrive);
  2284. msg.AddChar(L':');
  2285. }
  2286. msg += (PWSTR)(pszTemp + 48);
  2287. }
  2288. else {
  2289. if (VolData.vFileName.GetBuffer()) {
  2290. msg += VolData.vFileName;
  2291. }
  2292. }
  2293. Trace(error, "End: Volume Defragmentation. Errors encountered "
  2294. "while scanning the volume (last file processed: %ws)",
  2295. VolData.vFileName.GetBuffer());
  2296. // send error info to client
  2297. SendErrData(msg.GetBuffer(), ENGERR_GENERAL);
  2298. }
  2299. // Trigger an abort.
  2300. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  2301. // Set the event to signaled, allowing the UI to proceed
  2302. if (hDefragCompleteEvent){
  2303. SetEvent(hDefragCompleteEvent);
  2304. }
  2305. // There isn't much else to do.
  2306. ExitThread(0);
  2307. return TRUE;
  2308. }
  2309. // Exit if the controller (UI) wants us to stop.
  2310. if (TERMINATE == VolData.EngineState) {
  2311. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2312. // Set the event to signaled, allowing the UI to proceed
  2313. if (hDefragCompleteEvent){
  2314. SetEvent(hDefragCompleteEvent);
  2315. }
  2316. ExitThread(0);
  2317. }
  2318. if (!bCommandLineBootOptimizeFlag) {
  2319. //Send the report text data to the UI.
  2320. SendReportData();
  2321. }
  2322. //
  2323. // More initialisation, to update the display and get the Volume bitmap.
  2324. //
  2325. bResult = InitializeDefrag();
  2326. if (!bResult) {
  2327. LOG_ERR();
  2328. Trace(error, "End: Volume Defragmentation. Errors encountered while "
  2329. "initializing defrag engine.");
  2330. // Set the event to signaled, allowing the UI to proceed
  2331. if (hDefragCompleteEvent){
  2332. SetEvent(hDefragCompleteEvent);
  2333. }
  2334. return FALSE;
  2335. }
  2336. //
  2337. // Optimise the files listed by the prefetcher in layout.ini. Note that
  2338. // we currently only do this for the boot volume.
  2339. //
  2340. uEngineState = DEFRAG_STATE_BOOT_OPTIMIZING;
  2341. uLastPercentDone = 0;
  2342. uPercentDone = 1;
  2343. SendStatusData();
  2344. dwLayoutErrorCode = ProcessBootOptimise();
  2345. Trace(log, "Bootoptimize returned %lu", dwLayoutErrorCode);
  2346. // Exit if the controller (UI) wants us to stop.
  2347. if (TERMINATE == VolData.EngineState) {
  2348. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2349. // Set the event to signaled, allowing the UI to proceed
  2350. if (hDefragCompleteEvent){
  2351. SetEvent(hDefragCompleteEvent);
  2352. }
  2353. ExitThread(0);
  2354. }
  2355. // If command line was boot optimize -b /b, do the boot optimise only
  2356. if (bCommandLineBootOptimizeFlag) {
  2357. // If we failed layout optimization, tell the client.
  2358. if (ENG_NOERR != dwLayoutErrorCode) {
  2359. SendErrData(TEXT(""), dwLayoutErrorCode);
  2360. }
  2361. // Signal the client that we are done.
  2362. if (hDefragCompleteEvent){
  2363. SetEvent(hDefragCompleteEvent);
  2364. }
  2365. // And we're done!
  2366. Trace(log, "End: Volume Defragmentation. Boot Optimize status: %lu",
  2367. dwLayoutErrorCode);
  2368. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  2369. ExitThread(0);
  2370. return TRUE;
  2371. }
  2372. //
  2373. // Defrag the MFT next. This may help with our defragmentation perf, since
  2374. // there will presumably be fewer seeks while we find file fragments. (Of
  2375. // course, it would have been better if we could have done this before
  2376. // ScanNtfs, but still ...)
  2377. //
  2378. // We don't have a separate uEngineState for MFT Defag, but it's usually
  2379. // pretty fast.
  2380. //
  2381. MFTDefrag(
  2382. VolData.hVolume,
  2383. VolData.BitmapSize,
  2384. VolData.BytesPerSector,
  2385. VolData.TotalClusters,
  2386. VolData.MftZoneStart,
  2387. VolData.MftZoneEnd,
  2388. VolData.cDrive,
  2389. VolData.ClustersPerFRS
  2390. );
  2391. //
  2392. // Update the MFT stats after moving the MFT. This routine fills in some
  2393. // important VolData fields (such as TotalClusters) that we use later on;
  2394. // a failure here is fatal.
  2395. //
  2396. bResult = GetNtfsVolumeStats();
  2397. if (!bResult) {
  2398. LOG_ERR();
  2399. Trace(error, "End: Volume Defragmentation. Errors encountered while "
  2400. "updating NTFS volume statistics.");
  2401. return FALSE;
  2402. }
  2403. //
  2404. // If this is the command-line, check to ensure that at least 15% of the
  2405. // volume is free (unless, of course, the user specified the /f flag).
  2406. //
  2407. // Note that we only have to do this for the command-line: for the MMC
  2408. // snap-in, dfrgui appears to do this in CVolume::WarnFutility. Isn't
  2409. // consistency wonderful?
  2410. //
  2411. if ((bCommandLineMode) && (!bCommandLineForceFlag)) {
  2412. TCHAR szMsg[800];
  2413. bResult = ValidateFreeSpace(
  2414. bCommandLineMode,
  2415. VolData.FreeSpace,
  2416. VolData.UsableFreeSpace,
  2417. (VolData.TotalClusters * VolData.BytesPerCluster),
  2418. VolData.cDisplayLabel,
  2419. szMsg,
  2420. sizeof(szMsg)/sizeof(TCHAR)
  2421. );
  2422. if (!bResult) {
  2423. //
  2424. // Not enough free space. Send the error to the client, and
  2425. // trigger an abort (unlike the snap-in, the user doesn't get
  2426. // to answer y/n in the command-line; he has to re-run this
  2427. // with the /f flag to get any further).
  2428. //
  2429. SendErrData(szMsg, ENGERR_LOW_FREESPACE);
  2430. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  2431. // set the event to signaled, allowing the UI to proceed
  2432. if (hDefragCompleteEvent){
  2433. SetEvent(hDefragCompleteEvent);
  2434. }
  2435. ExitThread(0);
  2436. return TRUE;
  2437. }
  2438. }
  2439. //0.0E00 Defragment the Drive.
  2440. uEngineState = DEFRAG_STATE_DEFRAGMENTING;
  2441. uPercentDone = 3;
  2442. SendStatusData();
  2443. // Finally, start actually defragmenting the volume.
  2444. uConsolidatePercentDone = 3;
  2445. bResult = DefragNtfs();
  2446. if (!bResult) {
  2447. //
  2448. // If we detected any MFT corruption, put up a message asking the user
  2449. // to run chkdsk. Otherwise, just put up a generic failure message.
  2450. // Note that we may actually detect an MFT corruption here that the
  2451. // ScanNtfs part didn't, since we do more with the MFT here.
  2452. //
  2453. if (VolData.bMFTCorrupt) {
  2454. DWORD_PTR dwParams[3];
  2455. TCHAR szMsg[500];
  2456. TCHAR cString[500];
  2457. //
  2458. // IDS_CORRUPT_MFT - "Defragmentation of %1!s! has been aborted due
  2459. // to inconsistencies that were detected in the filesystem. Please
  2460. // run CHKDSK or SCANDISK on %2!s! to repair these inconsistencies,
  2461. // then run Disk Defragmenter again"
  2462. //
  2463. dwParams[0] = (DWORD_PTR) VolData.cDisplayLabel;
  2464. dwParams[1] = (DWORD_PTR) VolData.cDisplayLabel;
  2465. dwParams[2] = 0;
  2466. bResult = FormatMessage(
  2467. FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  2468. GetString(szMsg, sizeof(szMsg)/sizeof(TCHAR),
  2469. IDS_CORRUPT_MFT, GetDfrgResHandle()),
  2470. 0,
  2471. 0,
  2472. cString,
  2473. sizeof(cString)/sizeof(TCHAR),
  2474. (va_list*)dwParams);
  2475. if (!bResult) {
  2476. SendErrData(NULL, ENGERR_CORRUPT_MFT);
  2477. }
  2478. else {
  2479. SendErrData(cString, ENGERR_CORRUPT_MFT);
  2480. }
  2481. }
  2482. // There isn't much we can do other than trigger an abort.
  2483. PostMessage(hwndMain, WM_COMMAND, ID_ABORT, 0);
  2484. // set the event to signaled, allowing the UI to proceed
  2485. if (hDefragCompleteEvent){
  2486. SetEvent(hDefragCompleteEvent);
  2487. }
  2488. ExitThread(0);
  2489. return TRUE;
  2490. }
  2491. // Exit if the controller (UI) wants us to stop.
  2492. if (TERMINATE == VolData.EngineState) {
  2493. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2494. ExitThread(0);
  2495. }
  2496. uPercentDone = 98;
  2497. SendStatusData();
  2498. //
  2499. // Defrag the MFT again since all our file-moves above could have
  2500. // potentially fragmented it again
  2501. MFTDefrag(
  2502. VolData.hVolume,
  2503. VolData.BitmapSize,
  2504. VolData.BytesPerSector,
  2505. VolData.TotalClusters,
  2506. VolData.MftZoneStart,
  2507. VolData.MftZoneEnd,
  2508. VolData.cDrive,
  2509. VolData.ClustersPerFRS
  2510. );
  2511. //
  2512. // Update the MFT stats after moving the MFT.
  2513. //
  2514. bResult = GetNtfsVolumeStats();
  2515. if (!bResult) {
  2516. LOG_ERR();
  2517. Trace(error, "End: Volume Defragmentation. Errors encountered while "
  2518. "updating final NTFS volume statistics.");
  2519. return FALSE;
  2520. }
  2521. // Statistical Info: Note the end time for that pass.
  2522. GetLocalTime(&VolData.EndTime);
  2523. // Write the statistics to the screen AFTER defrag.
  2524. DisplayNtfsVolumeStats();
  2525. //
  2526. // Now clean-up the extent buffer. This will purge it as well, so we'll
  2527. // have a fully up-to-date DiskView of the disk.
  2528. //
  2529. // (ignoring return value)
  2530. DestroyExtentBuffer();
  2531. //
  2532. // Send stuff (status, bitmap, report) to the UI
  2533. //
  2534. uEngineState = DEFRAG_STATE_DEFRAGMENTED;
  2535. uPercentDone = 100;
  2536. SendStatusData();
  2537. SendGraphicsData();
  2538. SendReportData();
  2539. //Send the most fragged list to the UI.
  2540. SendMostFraggedList(FALSE);
  2541. // All done, close down now.
  2542. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2543. // Set the event to signaled, allowing the UI to proceed
  2544. if (hDefragCompleteEvent){
  2545. SetEvent(hDefragCompleteEvent);
  2546. }
  2547. //Kill the thread.
  2548. Trace(log, "End: Volume Defragmentation for %ws", VolData.cDisplayLabel);
  2549. ExitThread(0);
  2550. return TRUE;
  2551. }
  2552. /*******************************************************************************
  2553. ROUTINE DESCRIPTION:
  2554. Routine that carries out the defragmentation of a drive.
  2555. INPUT + OUTPUT:
  2556. None.
  2557. GLOBALS:
  2558. IN OUT Various VolData fields.
  2559. RETURN:
  2560. TRUE - Success.
  2561. FALSE - Fatal Error.
  2562. */
  2563. BOOL
  2564. DefragmentFiles(
  2565. IN CONST UINT uDefragmentPercentProgressFactor,
  2566. OUT LONGLONG *pSmallestFragmentedFileSize
  2567. )
  2568. {
  2569. ULONGLONG uFileCount = 0;
  2570. BOOL bAllFilesDone = TRUE, // set to true when we're fully done
  2571. bResult = TRUE,
  2572. bUsePerfCounter = TRUE;
  2573. BOOLEAN bRestart = TRUE;
  2574. LARGE_INTEGER CurrentCounter,
  2575. LastStatusUpdate,
  2576. LastSnapshotCheck,
  2577. CounterFrequency;
  2578. Trace(log, "Start: Defragmenting Files. Total fragmented file count: %lu",
  2579. RtlNumberGenericTableElementsAvl(&VolData.FragmentedFileTable));
  2580. *pSmallestFragmentedFileSize = 0;
  2581. // Exit if the controller wants us to stop.
  2582. if (TERMINATE == VolData.EngineState) {
  2583. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2584. ExitThread(0);
  2585. }
  2586. // If there aren't any fragmented files, we're done
  2587. if (0 == RtlNumberGenericTableElementsAvl(&VolData.FragmentedFileTable)) {
  2588. Trace(log, "End: Defragmenting Files. No fragmented files");
  2589. return TRUE;
  2590. }
  2591. uPass = 1;
  2592. SendStatusData();
  2593. do {
  2594. //
  2595. // Let's find the size of the first (smallest) fragmented file.
  2596. // This will allow us to skip any free space chunks that are smaller
  2597. // than it
  2598. //
  2599. bResult = GetNextNtfsFile(&VolData.FragmentedFileTable, bRestart);
  2600. bRestart = FALSE;
  2601. } while (bResult && !VolData.bFragmented);
  2602. bRestart = TRUE; // Reset this, so that the enumeration below re-starts
  2603. //
  2604. // Now build our free space list. This will ignore any free space chunks
  2605. // that are smaller than VolData.NumberOfClusters clusters (which currently
  2606. // contains the size of the smallest fragmented file)
  2607. //
  2608. bResult = BuildFreeSpaceList(&VolData.FreeSpaceTable, VolData.NumberOfClusters, TRUE);
  2609. if (!bResult) {
  2610. // A memory allocation failed, or some other error occured
  2611. ClearFreeSpaceTable();
  2612. Trace(log, "End: Defragmenting Files. Unable to build free space list");
  2613. return FALSE;
  2614. }
  2615. CurrentCounter.QuadPart = 0;
  2616. LastStatusUpdate.QuadPart = 0;
  2617. LastSnapshotCheck.QuadPart = 0;
  2618. //
  2619. // Check if the performance counters are available: we'll use this later
  2620. // to periodically update the status line and check for snapshots
  2621. //
  2622. if (!QueryPerformanceFrequency(&CounterFrequency) ||
  2623. (0 == CounterFrequency.QuadPart) ||
  2624. !QueryPerformanceCounter(&CurrentCounter)
  2625. ) {
  2626. bUsePerfCounter = FALSE;
  2627. Trace(log, "QueryPerformaceFrequency failed, will update status "
  2628. "line based on file count");
  2629. }
  2630. //
  2631. // This loop will break when we run out of fragmented files or freespace
  2632. //
  2633. while (TRUE) {
  2634. __try {
  2635. // Sleep if paused.
  2636. while (PAUSED == VolData.EngineState) {
  2637. Sleep(1000);
  2638. }
  2639. // Exit if the controller wants us to stop.
  2640. if (TERMINATE == VolData.EngineState) {
  2641. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2642. ExitThread(0);
  2643. }
  2644. //
  2645. // If we have perf counters available, we can use it to update the
  2646. // status line once in 4 seconds, and check for snapshots once in
  2647. // 64 seconds.
  2648. //
  2649. // If perf counters are not available, we have to fall back to the
  2650. // old way of updating the status once every 8 files, and checking
  2651. // for snapshots once every 128 files
  2652. //
  2653. ++uFileCount;
  2654. if (bUsePerfCounter && QueryPerformanceCounter(&CurrentCounter)) {
  2655. if ((CurrentCounter.QuadPart - LastStatusUpdate.QuadPart) >
  2656. (4 * CounterFrequency.QuadPart)) {
  2657. uDefragmentPercentDone =
  2658. (UINT) (uDefragmentPercentProgressFactor * VolData.FraggedClustersDone / VolData.InitialFraggedClusters);
  2659. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  2660. LastStatusUpdate.QuadPart = CurrentCounter.QuadPart;
  2661. SendStatusData();
  2662. if ((CurrentCounter.QuadPart - LastSnapshotCheck.QuadPart) > (64 * CounterFrequency.QuadPart)) {
  2663. LastSnapshotCheck.QuadPart = CurrentCounter.QuadPart;
  2664. PauseOnVolumeSnapshot(VolData.cVolumeName);
  2665. }
  2666. }
  2667. }
  2668. else if (uFileCount % 32) {
  2669. uDefragmentPercentDone =
  2670. (UINT) (uDefragmentPercentProgressFactor * VolData.FraggedClustersDone / VolData.InitialFraggedClusters);
  2671. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  2672. SendStatusData();
  2673. if (uFileCount % 128) {
  2674. PauseOnVolumeSnapshot(VolData.cVolumeName);
  2675. }
  2676. }
  2677. //
  2678. // Okay, find the next file to defragment. We need the check to
  2679. // sure that the file returned is in fact fragmented (though we're
  2680. // checking the fragmented table, which should theoritically
  2681. // contain only the fragmented files to begin with) since it is
  2682. // possible that on our previous pass, we successfully defragmented
  2683. // a file but were unable to remove it from this table for some
  2684. // reason (ie UpdateTables failed).
  2685. //
  2686. do {
  2687. bResult = GetNextNtfsFile(&VolData.FragmentedFileTable, bRestart);
  2688. bRestart = FALSE;
  2689. } while (bResult && !VolData.bFragmented);
  2690. if (!bResult) {
  2691. //
  2692. // We're out of fragmented files
  2693. //
  2694. Trace(log, "End: Defragmenting Files. Out of fragmented files");
  2695. break;
  2696. }
  2697. // Get the extent list & number of fragments in the file.
  2698. if (GetExtentList(DEFAULT_STREAMS, NULL)) {
  2699. if (FindFreeSpace(&VolData.FreeSpaceTable, TRUE, VolData.TotalClusters)) {
  2700. if (VolData.NumberOfClusters > (256 * 1024 / VolData.BytesPerCluster) * 1024) {
  2701. //
  2702. // If we're going to move a huge file (>256 MB), it is
  2703. // going to take a while, so make sure the status bar has the
  2704. // correct file name
  2705. //
  2706. GetNtfsFilePath();
  2707. SendStatusData();
  2708. }
  2709. if (MoveNtfsFile()) {
  2710. //
  2711. // We moved the file. Yay! Let's move this file to our
  2712. // ContiguousFiles tree.
  2713. //
  2714. // Ignoring return value. If this fails, it means that
  2715. // we couldn't move this file to the contiguous list.
  2716. // This is okay for now, we'll ignore it for now and try
  2717. // again the next time.
  2718. //
  2719. VolData.FraggedClustersDone += VolData.NumberOfRealClusters;
  2720. (void) UpdateFileTables(&VolData.FragmentedFileTable, &VolData.ContiguousFileTable);
  2721. }
  2722. else {
  2723. //
  2724. // We couldn't move this file for some reason. It might be
  2725. // because what we thought was a free region isn't any longer
  2726. // (the user's creating new files while we're defragging?)
  2727. //
  2728. Trace(warn, "Movefile failed. File StartingLcn:%I64d ClusterCount:%I64d (%lu)",
  2729. VolData.pFileListEntry->StartingLcn,
  2730. VolData.pFileListEntry->ClusterCount,
  2731. VolData.Status
  2732. );
  2733. if (VolData.Status == ERROR_RETRY) {
  2734. VolData.pFreeSpaceEntry->ClusterCount = 0;
  2735. }
  2736. bAllFilesDone = FALSE;
  2737. }
  2738. }
  2739. else {
  2740. //
  2741. // Sigh. No free space chunk that's big enough to hold all
  2742. // the extents of this file. It's no point enumerating more
  2743. // files for this pass, since they'll be bigger than the
  2744. // current file. Let's bail out of here, and hope that we
  2745. // can consolidate some free space.
  2746. //
  2747. GetNtfsFilePath();
  2748. Trace(log, "End: Defragmenting Files. Not enough free space remaining for this file (%I64u clusters) (%ws)",
  2749. VolData.NumberOfClusters, VolData.vFileName.GetBuffer());
  2750. *pSmallestFragmentedFileSize = VolData.NumberOfClusters;
  2751. bAllFilesDone = FALSE;
  2752. break;
  2753. }
  2754. }
  2755. else {
  2756. //
  2757. // We couldn't get the extents for this file. Ignore the
  2758. // file for now, and let's pick it up on our next pass.
  2759. //
  2760. bAllFilesDone = FALSE;
  2761. *pSmallestFragmentedFileSize = VolData.NumberOfClusters;
  2762. }
  2763. }
  2764. __finally {
  2765. if(VolData.hFile != INVALID_HANDLE_VALUE) {
  2766. CloseHandle(VolData.hFile);
  2767. VolData.hFile = INVALID_HANDLE_VALUE;
  2768. }
  2769. if(VolData.hFreeExtents != NULL) {
  2770. while(GlobalUnlock(VolData.hFreeExtents))
  2771. ;
  2772. EH_ASSERT(GlobalFree(VolData.hFreeExtents) == NULL);
  2773. VolData.hFreeExtents = NULL;
  2774. }
  2775. // update cluster array
  2776. PurgeExtentBuffer();
  2777. }
  2778. }
  2779. ClearFreeSpaceTable();
  2780. Trace(log, "End: Defragmenting Files. Processed %I64d Files. bAllFilesDone is %d", uFileCount - 1, bAllFilesDone);
  2781. return bAllFilesDone;
  2782. }
  2783. /*******************************************************************************
  2784. ROUTINE DESCRIPTION:
  2785. Finds a region that is the best bet for moving files out of, to free up
  2786. one big chunk of free space.
  2787. INPUT:
  2788. MinimumLength - The minimum length, in clusters, that we're looking for. If
  2789. this routine isn't able to find a region that is at least as big, it
  2790. will return false.
  2791. dwDesparationFactor - A number indicating what percent of the region may be
  2792. in-use. If this is 10, we're looking for a region that is no
  2793. more than 10% in-use (i.e., is 90% free), while if this is 100, we don't
  2794. care even if the region we find is full of files at the moment.
  2795. OUTPUT:
  2796. RegionStart - This will contain the startingLCN for the region that is the
  2797. best bet
  2798. RegionEnd - This contains the LCN where the interesting region ends
  2799. GLOBALS:
  2800. IN OUT Various VolData fields.
  2801. RETURN:
  2802. TRUE - Success.
  2803. FALSE - We couldn't find a region that meets our needs
  2804. */
  2805. BOOL
  2806. FindRegionToConsolidate(
  2807. IN CONST LONGLONG MinimumLength,
  2808. IN CONST DWORD dwDesparationFactor,
  2809. OUT LONGLONG *pRegionStart,
  2810. OUT LONGLONG *pRegionEnd
  2811. )
  2812. {
  2813. BOOLEAN bRestart = TRUE;
  2814. BOOLEAN bNewElement = FALSE;
  2815. BOOL bResult = FALSE;
  2816. PFREE_SPACE_ENTRY pFree = NULL;
  2817. PFILE_LIST_ENTRY pFile = NULL;
  2818. PRTL_GENERIC_TABLE pFreeTable = &VolData.FreeSpaceTable,
  2819. pContiguousTable = &VolData.ContiguousFileTable;
  2820. DWORD dwBytesInUsePercent = 0;
  2821. LONGLONG regionLength = 0,
  2822. regionStart = 0,
  2823. currentLcn = 0,
  2824. bytesInUse = 0;
  2825. LONGLONG bestRegionStart = 0,
  2826. bestRegionLength = 0,
  2827. bestRegionBytesInUse = 0;
  2828. LONGLONG biggestFree = 0;
  2829. Trace(log, "Start: FindRegionToConsolidate");
  2830. *pRegionStart = 0;
  2831. *pRegionEnd = 0;
  2832. //
  2833. // First, build the free space info--sort it by start sector
  2834. //
  2835. bResult = BuildFreeSpaceList(&VolData.FreeSpaceTable, 0, FALSE, &biggestFree);
  2836. if (!bResult) {
  2837. //
  2838. // A memory allocation failed, or some other error occured
  2839. //
  2840. ClearFreeSpaceTable();
  2841. Trace(log, "End: FindRegionToConsolidate. Unable to build free space list");
  2842. return FALSE;
  2843. }
  2844. //
  2845. // For now, start from the beginning.
  2846. //
  2847. pFile = (PFILE_LIST_ENTRY) LastEntry(pContiguousTable);
  2848. pFree = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeTable, TRUE);
  2849. if (!pFile || !pFree) {
  2850. Trace(log, "End: FindRegionToConsolidate (File:0x%x FreeSpace:0x%x)", pFile, pFree);
  2851. ClearFreeSpaceTable();
  2852. return FALSE;
  2853. }
  2854. //
  2855. // Go through the contiguous files and free-space regions, to keep track
  2856. // of the largest contiguous chunk we can find.
  2857. //
  2858. while (pFile && pFree) {
  2859. currentLcn = pFree->StartingLcn;
  2860. regionStart = currentLcn;
  2861. bytesInUse = 0;
  2862. while ((pFree) && (currentLcn == pFree->StartingLcn)) {
  2863. currentLcn += (pFree->ClusterCount);
  2864. // Get the next free-space entry
  2865. pFree = (PFREE_SPACE_ENTRY) RtlEnumerateGenericTableAvl(pFreeTable, FALSE);
  2866. // And go through all the contiguous files before the
  2867. // the next free space entry
  2868. while ((pFile) && (pFile->StartingLcn < currentLcn)) {
  2869. pFile = (PFILE_LIST_ENTRY) PreviousEntry(pFile);
  2870. }
  2871. while ((pFile) && (currentLcn == pFile->StartingLcn)) {
  2872. if (pFile->ClusterCount > biggestFree) {
  2873. --currentLcn;
  2874. break;
  2875. }
  2876. bytesInUse += (pFile->ClusterCount);
  2877. currentLcn += (pFile->ClusterCount);
  2878. pFile = (PFILE_LIST_ENTRY) PreviousEntry(pFile);
  2879. }
  2880. }
  2881. if (!pFile && !pFree) {
  2882. //
  2883. // We reached the end of the disk
  2884. //
  2885. currentLcn = VolData.TotalClusters;
  2886. }
  2887. regionLength = currentLcn - regionStart;
  2888. if ((bytesInUse > 0) && (regionLength >= MinimumLength)){
  2889. dwBytesInUsePercent = (ULONG) ((bytesInUse * 100) / (regionLength));
  2890. // Try to find some interesting chunks.
  2891. if ((regionLength > biggestFree) &&
  2892. (regionLength > bestRegionLength) &&
  2893. (dwBytesInUsePercent < dwDesparationFactor)) {
  2894. //
  2895. // Okay, now, see if this is in fact better than the current chunk.
  2896. //
  2897. // if ((regionLength - bestRegionLength) > (bytesInUse - bestRegionBytesInUse) * 1) {
  2898. bestRegionStart = regionStart;
  2899. bestRegionLength = regionLength;
  2900. bestRegionBytesInUse = bytesInUse;
  2901. // }
  2902. }
  2903. }
  2904. }
  2905. ClearFreeSpaceTable();
  2906. if (bestRegionLength > 0) {
  2907. dwBytesInUsePercent = (ULONG) ((bestRegionBytesInUse * 100) / (bestRegionLength));
  2908. *pRegionStart = bestRegionStart;
  2909. *pRegionEnd = bestRegionStart + bestRegionLength;
  2910. }
  2911. Trace(log, "End: FindRegionToConsolidate (Region Start:%I64u Length:%I64u End:%I64u Used:%I64u(%lu%%))",
  2912. *pRegionStart, bestRegionLength, *pRegionEnd, bestRegionBytesInUse, dwBytesInUsePercent);
  2913. return (bestRegionLength > 0);
  2914. }
  2915. /*******************************************************************************
  2916. ROUTINE DESCRIPTION:
  2917. This routine attempts to free up space to form one big chunk
  2918. GLOBALS:
  2919. VolData
  2920. RETURN:
  2921. TRUE - We finished defragmenting the volume successfully.
  2922. FALSE - Some files on the volume could not be defragmented.
  2923. */
  2924. BOOL
  2925. ConsolidateFreeSpace(
  2926. IN CONST LONGLONG MinimumLength,
  2927. IN CONST DWORD dwDesparationFactor,
  2928. IN CONST BOOL bDefragMftZone,
  2929. IN CONST UINT uPercentProgress
  2930. )
  2931. {
  2932. LONGLONG MaxFreeSpaceChunkSize = VolData.TotalClusters,
  2933. CurrentFileSize = 0,
  2934. CurrentLcn = 0;
  2935. ULONGLONG uFileCount = 0;
  2936. BOOL bResult = TRUE,
  2937. bDone = FALSE,
  2938. bSuccess = TRUE,
  2939. bUsePerfCounter = TRUE;
  2940. BOOLEAN bRestart = TRUE;
  2941. UINT iCount = 0,
  2942. uConsolidatePercentDoneAtStart = 0;
  2943. FILE_LIST_ENTRY NewFileListEntry;
  2944. FREE_SPACE_ENTRY NewFreeSpaceEntry;
  2945. BOOLEAN bNewElement = FALSE;
  2946. PVOID p = NULL;
  2947. PFREE_SPACE_ENTRY pFreeSpace = NULL;
  2948. LARGE_INTEGER CurrentCounter,
  2949. LastStatusUpdate,
  2950. LastSnapshotCheck,
  2951. CounterFrequency;
  2952. LONGLONG RegionStartLcn = 0, RegionEndLcn = VolData.TotalClusters;
  2953. LARGE_INTEGER Test1, Test2;
  2954. Trace(log, "Start: Consolidating free space. Total contiguous file count: %lu",
  2955. RtlNumberGenericTableElementsAvl(&VolData.ContiguousFileTable));
  2956. uPass = 2;
  2957. uConsolidatePercentDoneAtStart = uConsolidatePercentDone;
  2958. SendStatusData();
  2959. ZeroMemory(&NewFileListEntry, sizeof(FILE_LIST_ENTRY));
  2960. //
  2961. // Terminate if told to stop by the controller -
  2962. // this is not an error.
  2963. //
  2964. if (TERMINATE == VolData.EngineState) {
  2965. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  2966. return TRUE;
  2967. }
  2968. if (bDefragMftZone) {
  2969. RegionStartLcn = VolData.MftZoneStart;
  2970. RegionEndLcn = VolData.MftZoneEnd;
  2971. if ((RegionStartLcn < VolData.BootOptimizeEndClusterExclude) &&
  2972. (RegionEndLcn > VolData.BootOptimizeBeginClusterExclude)) {
  2973. if(RegionStartLcn < VolData.BootOptimizeBeginClusterExclude) {
  2974. RegionEndLcn = VolData.BootOptimizeBeginClusterExclude;
  2975. }
  2976. else if(RegionEndLcn <= VolData.BootOptimizeEndClusterExclude) {
  2977. Trace(log, "End: Consolidating free space. MFT Zone fully within Boot-optimise zone");
  2978. return FALSE;
  2979. }
  2980. else {
  2981. //0.0E00 Handle the case of EndingLcn > pVolData->bootoptZoneEnd.
  2982. RegionStartLcn = VolData.BootOptimizeEndClusterExclude;
  2983. }
  2984. }
  2985. }
  2986. else {
  2987. if (!FindRegionToConsolidate(MinimumLength, dwDesparationFactor, &RegionStartLcn, &RegionEndLcn)) {
  2988. Trace(log, "End: Consolidating free space. Unable to find a suitable region");
  2989. return FALSE;
  2990. }
  2991. }
  2992. bResult = BuildFreeSpaceListWithExclude(&VolData.FreeSpaceTable, 0, RegionStartLcn, RegionEndLcn, TRUE);
  2993. if (!bResult) {
  2994. //
  2995. // A memory allocation failed, or some other error occured
  2996. //
  2997. ClearFreeSpaceTable();
  2998. Trace(log, "End: Consolidating free space. Unable to build free space list");
  2999. return FALSE;
  3000. }
  3001. CurrentLcn = RegionEndLcn;
  3002. pFreeSpace = (PFREE_SPACE_ENTRY)LastEntry(&VolData.FreeSpaceTable);
  3003. Trace(log, "Largest Free Space : %I64u", pFreeSpace->ClusterCount);
  3004. NewFileListEntry.FileRecordNumber = (-1);
  3005. NewFileListEntry.StartingLcn = RegionEndLcn;
  3006. CurrentCounter.QuadPart = 0;
  3007. LastStatusUpdate.QuadPart = 0;
  3008. LastSnapshotCheck.QuadPart = 0;
  3009. //
  3010. // Check if the performance counters are available: we'll use this later
  3011. // to periodically update the status line and check for snapshots
  3012. //
  3013. if (!QueryPerformanceFrequency(&CounterFrequency) || (0 == CounterFrequency.QuadPart) || !QueryPerformanceCounter(&CurrentCounter)) {
  3014. Trace(log, "QueryPerformaceFrequency failed, will update status line based on file count");
  3015. bUsePerfCounter = FALSE;
  3016. }
  3017. //
  3018. //
  3019. // Now, let's start from the end of the disk, and attempt to move
  3020. // the files to free spaces earlier.
  3021. //
  3022. while (bResult) {
  3023. // Sleep if paused.
  3024. while (PAUSED == VolData.EngineState) {
  3025. Sleep(1000);
  3026. }
  3027. // Terminate if told to stop by the controller - this is not an error.
  3028. if (TERMINATE == VolData.EngineState) {
  3029. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3030. break;
  3031. }
  3032. //
  3033. // If we have perf counters available, we can use it to update the
  3034. // status line once in 4 seconds, and check for snapshots once in
  3035. // 64 seconds.
  3036. //
  3037. // If perf counters are not available, we have to fall back to the
  3038. // old way of updating the status once every 8 files, and checking
  3039. // for snapshots once every 128 files
  3040. //
  3041. ++uFileCount;
  3042. if (bUsePerfCounter && QueryPerformanceCounter(&CurrentCounter)) {
  3043. if ((uFileCount > 1) && ((CurrentCounter.QuadPart - LastStatusUpdate.QuadPart) >
  3044. (4 * CounterFrequency.QuadPart))) {
  3045. uConsolidatePercentDone = uConsolidatePercentDoneAtStart +
  3046. (UINT) (((RegionStartLcn - CurrentLcn) * uPercentProgress) / (RegionEndLcn - RegionStartLcn));
  3047. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  3048. LastStatusUpdate.QuadPart = CurrentCounter.QuadPart;
  3049. SendStatusData();
  3050. if ((CurrentCounter.QuadPart - LastSnapshotCheck.QuadPart) > (64 * CounterFrequency.QuadPart)) {
  3051. LastSnapshotCheck.QuadPart = CurrentCounter.QuadPart;
  3052. PauseOnVolumeSnapshot(VolData.cVolumeName);
  3053. }
  3054. }
  3055. }
  3056. else if (uFileCount % 32) {
  3057. uConsolidatePercentDone = uConsolidatePercentDoneAtStart +
  3058. (UINT)(((RegionStartLcn - CurrentLcn) * uPercentProgress) / (RegionEndLcn - RegionStartLcn));
  3059. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  3060. SendStatusData();
  3061. if (uFileCount % 128) {
  3062. PauseOnVolumeSnapshot(VolData.cVolumeName);
  3063. }
  3064. }
  3065. //
  3066. // Okay, find the next file to move. Note that the first file we find will be from
  3067. // the region of interest
  3068. //
  3069. bResult = GetNextNtfsFile(&VolData.ContiguousFileTable, bRestart, MaxFreeSpaceChunkSize, &NewFileListEntry);
  3070. bRestart = FALSE;
  3071. CurrentFileSize = VolData.NumberOfClusters;
  3072. CurrentLcn = VolData.StartingLcn;
  3073. if (VolData.StartingLcn < RegionStartLcn) {
  3074. break;
  3075. }
  3076. if (bResult) {
  3077. if ((VolData.pFileListEntry->Flags & FLE_BOOTOPTIMISE) &&
  3078. (VolData.StartingLcn > VolData.BootOptimizeBeginClusterExclude) &&
  3079. (VolData.StartingLcn < VolData.BootOptimizeEndClusterExclude)) {
  3080. continue;
  3081. }
  3082. }
  3083. if (bResult) {
  3084. // Get the extent list & number of fragments in the file.
  3085. if (GetExtentList(DEFAULT_STREAMS, NULL)) {
  3086. bDone = FALSE;
  3087. while (!bDone) {
  3088. bDone = TRUE;
  3089. if (FindSortedFreeSpace(&VolData.FreeSpaceTable)) {
  3090. //
  3091. // Found a free space chunk that was big enough. If
  3092. // it's before the file, move the file towards the
  3093. // start of the disk
  3094. //
  3095. CopyMemory(&NewFreeSpaceEntry, VolData.pFreeSpaceEntry, sizeof(FREE_SPACE_ENTRY));
  3096. bNewElement = RtlDeleteElementGenericTable(&VolData.FreeSpaceTable, (PVOID)VolData.pFreeSpaceEntry);
  3097. if (!bNewElement) {
  3098. Trace(warn, "Could not find Element in Free Space Table!");
  3099. assert(FALSE);
  3100. }
  3101. VolData.pFreeSpaceEntry = &NewFreeSpaceEntry;
  3102. CopyMemory(&NewFileListEntry, VolData.pFileListEntry, sizeof(FILE_LIST_ENTRY));
  3103. bNewElement = RtlDeleteElementGenericTable(&VolData.ContiguousFileTable, (PVOID)VolData.pFileListEntry);
  3104. if (!bNewElement) {
  3105. Trace(warn, "Could not find Element in ContiguousFileTable!");
  3106. assert(FALSE);
  3107. }
  3108. VolData.pFileListEntry = &NewFileListEntry;
  3109. if (MoveNtfsFile()) {
  3110. NewFileListEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  3111. VolData.pFreeSpaceEntry->StartingLcn += VolData.NumberOfClusters;
  3112. VolData.pFreeSpaceEntry->ClusterCount -= VolData.NumberOfClusters;
  3113. }
  3114. else {
  3115. Trace(warn, "Movefile failed. File StartingLcn:%I64d ClusterCount:%I64d FreeSpace StartingLcn:%I64d ClusterCount:%I64d Status:%lu",
  3116. VolData.pFileListEntry->StartingLcn,
  3117. VolData.pFileListEntry->ClusterCount,
  3118. VolData.pFreeSpaceEntry->StartingLcn,
  3119. VolData.pFreeSpaceEntry->ClusterCount,
  3120. VolData.Status
  3121. );
  3122. if (VolData.Status == ERROR_RETRY) {
  3123. VolData.pFreeSpaceEntry->ClusterCount = 0;
  3124. bDone = FALSE;
  3125. }
  3126. else if (bDefragMftZone) {
  3127. bResult = FALSE;
  3128. bSuccess = FALSE;
  3129. break;
  3130. }
  3131. }
  3132. if (VolData.pFreeSpaceEntry->ClusterCount > 0) {
  3133. // Add this file to the contiguous-files table
  3134. p = RtlInsertElementGenericTable(
  3135. &VolData.FreeSpaceTable,
  3136. (PVOID) VolData.pFreeSpaceEntry,
  3137. sizeof(FREE_SPACE_ENTRY),
  3138. &bNewElement);
  3139. if (!p) {
  3140. // An allocation failed
  3141. Trace(log, "End: Consolidating free space. Unable to add back a free space to the freespace table");
  3142. assert(FALSE);
  3143. bResult = FALSE;
  3144. bSuccess = FALSE;
  3145. break;
  3146. };
  3147. }
  3148. // Add this file to the contiguous-files table
  3149. p = RtlInsertElementGenericTable(
  3150. &VolData.ContiguousFileTable,
  3151. (PVOID) VolData.pFileListEntry,
  3152. sizeof(FILE_LIST_ENTRY),
  3153. &bNewElement);
  3154. if (!p) {
  3155. // An allocation failed
  3156. Trace(log, "End: Consolidating free space. Unable to add back a file to the contiguous file table");
  3157. assert(FALSE);
  3158. bResult = FALSE;
  3159. bSuccess = FALSE;
  3160. break;
  3161. };
  3162. }
  3163. else {
  3164. Trace(log, "End: Consolidating free space. Unable to move file out Start:%I64u Length:%I64u (%lu)",
  3165. VolData.StartingLcn, VolData.NumberOfClusters, iCount);
  3166. if ((bDefragMftZone) || (++iCount > 10)) {
  3167. bSuccess = FALSE;
  3168. bResult = FALSE;
  3169. break;
  3170. }
  3171. }
  3172. }
  3173. }
  3174. else if (bDefragMftZone) {
  3175. bSuccess = FALSE;
  3176. bResult = FALSE;
  3177. }
  3178. if(VolData.hFile != INVALID_HANDLE_VALUE) {
  3179. CloseHandle(VolData.hFile);
  3180. VolData.hFile = INVALID_HANDLE_VALUE;
  3181. }
  3182. if(VolData.hFreeExtents != NULL) {
  3183. while(GlobalUnlock(VolData.hFreeExtents))
  3184. ;
  3185. EH_ASSERT(GlobalFree(VolData.hFreeExtents) == NULL);
  3186. VolData.hFreeExtents = NULL;
  3187. }
  3188. // update cluster array
  3189. PurgeExtentBuffer();
  3190. }
  3191. }
  3192. ClearFreeSpaceTable();
  3193. Trace(log, "End: Consolidating free space. Processed %I64u files", uFileCount - 1);
  3194. return bSuccess;
  3195. }
  3196. /*****************************************************************************************************************
  3197. ROUTINE DESCRIPTION:
  3198. This routine attempts to free up space at the end of the disk
  3199. GLOBALS:
  3200. VolData
  3201. RETURN:
  3202. TRUE - We finished defragmenting the volume successfully.
  3203. FALSE - Some files on the volume could not be defragmented.
  3204. */
  3205. BOOL
  3206. MoveFilesForward(
  3207. IN CONST UINT uPercentProgress
  3208. )
  3209. {
  3210. LONGLONG MaxFreeSpaceChunkSize = VolData.TotalClusters,
  3211. CurrentFileSize = 0,
  3212. CurrentLcn = VolData.TotalClusters;
  3213. ULONGLONG uFileCount = 0;
  3214. BOOL bResult = TRUE,
  3215. bDone = FALSE,
  3216. bUsePerfCounter = TRUE;
  3217. BOOLEAN bRestart = TRUE;
  3218. FILE_LIST_ENTRY NewFileListEntry;
  3219. FREE_SPACE_ENTRY NewFreeSpaceEntry;
  3220. BOOLEAN bNewElement = FALSE;
  3221. PVOID p = NULL;
  3222. PFREE_SPACE_ENTRY pFreeSpace = NULL;
  3223. LARGE_INTEGER CurrentCounter,
  3224. LastStatusUpdate,
  3225. LastSnapshotCheck,
  3226. CounterFrequency;
  3227. UINT uConsolidatePercentDoneAtStart = 0;
  3228. Trace(log, "Start: Moving files forward. Total contiguous file count: %lu (percent done: %lu + %lu, %lu)",
  3229. RtlNumberGenericTableElementsAvl(&VolData.ContiguousFileTable),
  3230. uDefragmentPercentDone,
  3231. uConsolidatePercentDone,
  3232. uPercentProgress);
  3233. //
  3234. // Terminate if told to stop by the controller -
  3235. // this is not an error.
  3236. //
  3237. if (TERMINATE == VolData.EngineState) {
  3238. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3239. return TRUE;
  3240. }
  3241. uPass = 2;
  3242. uConsolidatePercentDoneAtStart = uConsolidatePercentDone;
  3243. SendStatusData();
  3244. ZeroMemory(&NewFreeSpaceEntry, sizeof(FREE_SPACE_ENTRY));
  3245. AllocateFreeSpaceListsWithMultipleTrees();
  3246. //
  3247. // First, build the free space info--sort it by start sector
  3248. //
  3249. bResult = BuildFreeSpaceListWithMultipleTrees(&MaxFreeSpaceChunkSize);
  3250. if (!bResult) {
  3251. //
  3252. // A memory allocation failed, or some other error occured
  3253. //
  3254. ClearFreeSpaceListWithMultipleTrees();
  3255. Trace(log, "End: Moving files forward. Unable to build free space list");
  3256. return FALSE;
  3257. }
  3258. CurrentCounter.QuadPart = 0;
  3259. LastStatusUpdate.QuadPart = 0;
  3260. LastSnapshotCheck.QuadPart = 0;
  3261. //
  3262. // Check if the performance counters are available: we'll use this later
  3263. // to periodically update the status line and check for snapshots
  3264. //
  3265. if (!QueryPerformanceFrequency(&CounterFrequency) || (0 == CounterFrequency.QuadPart) || !QueryPerformanceCounter(&CurrentCounter)) {
  3266. Trace(log, "QueryPerformaceFrequency failed, will update status line based on file count");
  3267. bUsePerfCounter = FALSE;
  3268. }
  3269. //
  3270. //
  3271. // Now, let's start from the end of the disk, and attempt to move
  3272. // the files to free spaces earlier.
  3273. //
  3274. while (bResult) {
  3275. // Sleep if paused.
  3276. while (PAUSED == VolData.EngineState) {
  3277. Sleep(1000);
  3278. }
  3279. // Terminate if told to stop by the controller - this is not an error.
  3280. if (TERMINATE == VolData.EngineState) {
  3281. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3282. break;
  3283. }
  3284. //
  3285. // If we have perf counters available, we can use it to update the
  3286. // status line once in 4 seconds, and check for snapshots once in
  3287. // 64 seconds.
  3288. //
  3289. // If perf counters are not available, we have to fall back to the
  3290. // old way of updating the status once every 8 files, and checking
  3291. // for snapshots once every 128 files
  3292. //
  3293. ++uFileCount;
  3294. if (bUsePerfCounter && QueryPerformanceCounter(&CurrentCounter)) {
  3295. if ((uFileCount > 1) && ((CurrentCounter.QuadPart - LastStatusUpdate.QuadPart) >
  3296. (4 * CounterFrequency.QuadPart))) {
  3297. uConsolidatePercentDone = uConsolidatePercentDoneAtStart +
  3298. (UINT)(((VolData.TotalClusters - CurrentLcn) * uPercentProgress) / VolData.TotalClusters);
  3299. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  3300. LastStatusUpdate.QuadPart = CurrentCounter.QuadPart;
  3301. SendStatusData();
  3302. if ((CurrentCounter.QuadPart - LastSnapshotCheck.QuadPart) > (64 * CounterFrequency.QuadPart)) {
  3303. LastSnapshotCheck.QuadPart = CurrentCounter.QuadPart;
  3304. PauseOnVolumeSnapshot(VolData.cVolumeName);
  3305. }
  3306. }
  3307. }
  3308. else if (uFileCount % 32) {
  3309. uConsolidatePercentDone = uConsolidatePercentDoneAtStart +
  3310. (UINT)(((VolData.TotalClusters - CurrentLcn) * uPercentProgress) / VolData.TotalClusters);
  3311. uPercentDone = uDefragmentPercentDone + uConsolidatePercentDone;
  3312. SendStatusData();
  3313. if (uFileCount % 128) {
  3314. PauseOnVolumeSnapshot(VolData.cVolumeName);
  3315. }
  3316. }
  3317. //
  3318. // Okay, find the next file to move
  3319. //
  3320. bResult = GetNextNtfsFile(&VolData.ContiguousFileTable, bRestart, MaxFreeSpaceChunkSize);
  3321. bRestart = FALSE;
  3322. CurrentFileSize = VolData.NumberOfClusters;
  3323. CurrentLcn = VolData.StartingLcn;
  3324. if (bResult) {
  3325. if ((VolData.pFileListEntry->Flags & FLE_BOOTOPTIMISE) &&
  3326. (VolData.StartingLcn > VolData.BootOptimizeBeginClusterExclude) &&
  3327. (VolData.StartingLcn < VolData.BootOptimizeEndClusterExclude)) {
  3328. continue;
  3329. }
  3330. }
  3331. if (bResult) {
  3332. // Get the extent list & number of fragments in the file.
  3333. if (GetExtentList(DEFAULT_STREAMS, NULL)) {
  3334. bDone = FALSE;
  3335. while (!bDone) {
  3336. bDone = TRUE;
  3337. if (FindFreeSpaceWithMultipleTrees(VolData.NumberOfClusters, VolData.StartingLcn)) {
  3338. //
  3339. // Found a free space chunk that was big enough. If
  3340. // it's before the file, move the file towards the
  3341. // start of the disk
  3342. //
  3343. CopyMemory(&NewFileListEntry, VolData.pFileListEntry, sizeof(FILE_LIST_ENTRY));
  3344. bNewElement = RtlDeleteElementGenericTable(&VolData.ContiguousFileTable, (PVOID)VolData.pFileListEntry);
  3345. if (!bNewElement) {
  3346. Trace(warn, "Could not find Element in ContiguousFileTable!");
  3347. assert(FALSE);
  3348. }
  3349. VolData.pFileListEntry = &NewFileListEntry;
  3350. if (MoveNtfsFile()) {
  3351. NewFileListEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  3352. NewFreeSpaceEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn + VolData.NumberOfClusters;
  3353. NewFreeSpaceEntry.ClusterCount = VolData.pFreeSpaceEntry->ClusterCount - VolData.NumberOfClusters;
  3354. UpdateInMultipleTrees(VolData.pFreeSpaceEntry, &NewFreeSpaceEntry);
  3355. VolData.pFreeSpaceEntry = NULL;
  3356. }
  3357. else {
  3358. Trace(warn, "Movefile failed. File StartingLcn:%I64d ClusterCount:%I64d FreeSpace:%I64d Len:%I64d Status:%lu",
  3359. VolData.pFileListEntry->StartingLcn,
  3360. VolData.pFileListEntry->ClusterCount,
  3361. VolData.pFreeSpaceEntry->StartingLcn,
  3362. VolData.pFreeSpaceEntry->ClusterCount,
  3363. VolData.Status
  3364. );
  3365. if (VolData.Status == ERROR_RETRY) {
  3366. NewFreeSpaceEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  3367. NewFreeSpaceEntry.ClusterCount = 0;
  3368. UpdateInMultipleTrees(VolData.pFreeSpaceEntry, NULL);
  3369. VolData.pFreeSpaceEntry = NULL;
  3370. bDone = FALSE;
  3371. }
  3372. }
  3373. // Add this file to the contiguous-files table
  3374. p = RtlInsertElementGenericTable(
  3375. &VolData.ContiguousFileTable,
  3376. (PVOID) VolData.pFileListEntry,
  3377. sizeof(FILE_LIST_ENTRY),
  3378. &bNewElement);
  3379. if (!p) {
  3380. // An allocation failed
  3381. Trace(log, "End: Moving files forward. Unable to add back a file to the contiguous file table");
  3382. return FALSE;
  3383. };
  3384. }
  3385. else {
  3386. //
  3387. // No free space before this file that's big enough
  3388. // to hold this file. This implies that the biggest
  3389. // free space chunk before this file is smaller than
  3390. // this file--so we should skip trying to find a
  3391. // free space chunk for any file that's bigger than
  3392. // this file before this file.
  3393. //
  3394. if (1 >= CurrentFileSize) {
  3395. //
  3396. // No free space chunk before this file at all
  3397. //
  3398. Trace(log, "No free space before Lcn %I64d",
  3399. VolData.pFileListEntry->StartingLcn);
  3400. bResult = FALSE;
  3401. break;
  3402. }
  3403. MaxFreeSpaceChunkSize = CurrentFileSize;
  3404. // Trace(log, "Resetting MaxFreeSpaceChunkSize to %I64d (StartingLcn:%I64d)",
  3405. // MaxFreeSpaceChunkSize, VolData.pFileListEntry->StartingLcn);
  3406. }
  3407. }
  3408. }
  3409. if(VolData.hFile != INVALID_HANDLE_VALUE) {
  3410. CloseHandle(VolData.hFile);
  3411. VolData.hFile = INVALID_HANDLE_VALUE;
  3412. }
  3413. if(VolData.hFreeExtents != NULL) {
  3414. while(GlobalUnlock(VolData.hFreeExtents))
  3415. ;
  3416. EH_ASSERT(GlobalFree(VolData.hFreeExtents) == NULL);
  3417. VolData.hFreeExtents = NULL;
  3418. }
  3419. // update cluster array
  3420. PurgeExtentBuffer();
  3421. }
  3422. }
  3423. ClearFreeSpaceListWithMultipleTrees();
  3424. Trace(log, "End: Moving files forward. Processed %I64u files", uFileCount - 1);
  3425. return TRUE;
  3426. }
  3427. /*****************************************************************************************************************
  3428. ROUTINE DESCRIPTION:
  3429. This moves between DefragmentFiles (which attempts to defrag fragmented
  3430. files), and ConsolidateFreeSpace (which attempts to move contiguous files
  3431. forward, towards the centre of the disk).
  3432. GLOBALS:
  3433. VolData
  3434. RETURN:
  3435. TRUE - We finished defragmenting the volume successfully.
  3436. FALSE - Some files on the volume could not be defragmented.
  3437. */
  3438. BOOL
  3439. DefragNtfs(
  3440. )
  3441. {
  3442. ULONG NumFragmentedFiles = 0,
  3443. PreviousFragmented = 0,
  3444. PreviousFragmented2 = 0;
  3445. UINT uPassCount = 0,
  3446. uMoveFilesCount = 0;
  3447. BOOL bDone = FALSE, bConsolidate = FALSE;
  3448. LONGLONG MinimumLength = 0;
  3449. BOOL bMftZoneDefragmented = FALSE;
  3450. UINT consolidateProgress = 0;
  3451. Trace(log, "Start: DefragNtfs");
  3452. do {
  3453. do {
  3454. //
  3455. // If the controller tells us to stop (user cancelled), do.
  3456. //
  3457. if (VolData.EngineState == TERMINATE) {
  3458. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3459. break;
  3460. }
  3461. Trace(log, "Starting pass %lu (defrag: %lu%% done, consolidate: %lu%% done)",
  3462. ++uPassCount, uDefragmentPercentDone, uConsolidatePercentDone);
  3463. //
  3464. // Attempt to Defragment fragmented files. This routine returns true
  3465. // if we successfully defragmented all the files on the volume.
  3466. //
  3467. bDone = DefragmentFiles(45, &MinimumLength);
  3468. NumFragmentedFiles = RtlNumberGenericTableElementsAvl(&VolData.FragmentedFileTable);
  3469. Trace(log, "After DefragmentFiles, NumFragmentedFiles: %lu, PreviousFragmented:%lu",
  3470. NumFragmentedFiles, PreviousFragmented);
  3471. if ((bDone) || (NumFragmentedFiles == PreviousFragmented)) {
  3472. //
  3473. // We're either done, or we didn't make any progress--fragmented
  3474. // file count didn't change from our last attempt.
  3475. //
  3476. break;
  3477. }
  3478. if (VolData.EngineState == TERMINATE) {
  3479. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3480. break;
  3481. }
  3482. //
  3483. // Try to consolidate some free space. This routine will try to
  3484. // move files out of a region and create a big free space chunk.
  3485. //
  3486. if ((uPassCount < 10) && (uPassCount % 2)) {
  3487. consolidateProgress = 1;
  3488. }
  3489. else {
  3490. consolidateProgress = 0;
  3491. }
  3492. bConsolidate = ConsolidateFreeSpace(MinimumLength, 75, FALSE, consolidateProgress);
  3493. PreviousFragmented = NumFragmentedFiles;
  3494. if (!bConsolidate) {
  3495. // We couldn't find/create a space big enough...
  3496. break;
  3497. }
  3498. if (!bMftZoneDefragmented) {
  3499. // If we haven't moved files out of the MFT Zone, now's a good
  3500. // time to do so
  3501. bMftZoneDefragmented = ConsolidateFreeSpace(0, 100, TRUE, 1);
  3502. }
  3503. }while (!bDone);
  3504. if ((bDone) || (NumFragmentedFiles == PreviousFragmented2)) {
  3505. break;
  3506. }
  3507. PreviousFragmented2 = NumFragmentedFiles;
  3508. if (TERMINATE == VolData.EngineState) {
  3509. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  3510. break;
  3511. }
  3512. ++uMoveFilesCount;
  3513. MoveFilesForward(25 / (uMoveFilesCount * 2));
  3514. ConsolidateFreeSpace(MinimumLength, 75, FALSE,0);
  3515. }while (TRUE);
  3516. if ((bDone) && (TERMINATE != VolData.EngineState)) {
  3517. //
  3518. // We're done on our first attempt above, we should consolidate at
  3519. // least once
  3520. //
  3521. if (!bMftZoneDefragmented) {
  3522. bMftZoneDefragmented = ConsolidateFreeSpace(0, 100, TRUE, 1);
  3523. }
  3524. MoveFilesForward((98 - uPercentDone));
  3525. }
  3526. VolData.bFragmented = !bDone;
  3527. Trace(log, "End: DefragNtfs. Complete:%lu", bDone);
  3528. return TRUE;
  3529. }
  3530. /*****************************************************************************************************************
  3531. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3532. ROUTINE DESCRIPTION:
  3533. After a place has been found to move this file to, this function will move it there.
  3534. GLOBALS:
  3535. VolData
  3536. RETURN:
  3537. TRUE - Success.
  3538. FALSE - Fatal Error.
  3539. */
  3540. BOOL
  3541. MoveNtfsFile(
  3542. )
  3543. {
  3544. // Message(TEXT("MoveNtfsFile"), -1, NULL);
  3545. if((!VolData.hFile) || (VolData.hFile == INVALID_HANDLE_VALUE)) {
  3546. // Get the file name
  3547. if(!GetNtfsFilePath() || (!VolData.vFileName.GetBuffer())) {
  3548. return FALSE;
  3549. }
  3550. // Check if file is in exclude list
  3551. if(!CheckFileForExclude()) {
  3552. return FALSE;
  3553. }
  3554. // Get a handle to the file
  3555. if(!OpenNtfsFile()) {
  3556. return FALSE;
  3557. }
  3558. }
  3559. //SendStatusData();
  3560. // pVolData->Status already set.
  3561. return MoveFile();
  3562. }
  3563. /*****************************************************************************************************************
  3564. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3565. ROUTINE DESCRIPTION:
  3566. Partially defrags an NTFS file.
  3567. GLOBALS:
  3568. VolData
  3569. RETURN:
  3570. TRUE - Success.
  3571. FALSE - Fatal Error.
  3572. */
  3573. BOOL
  3574. PartialDefragNtfs(
  3575. )
  3576. {
  3577. Message(TEXT("PartialDefragNtfs"), -1, NULL);
  3578. // Check to see if there is enough free space.
  3579. if(VolData.hFreeExtents == NULL) {
  3580. return TRUE;
  3581. }
  3582. if(VolData.hFile == INVALID_HANDLE_VALUE) {
  3583. // Get the file name
  3584. if(!GetNtfsFilePath()) {
  3585. VolData.Status = NEXT_ALGO_STEP;
  3586. return FALSE;
  3587. }
  3588. // Check if file is in exclude list
  3589. if(!CheckFileForExclude()) {
  3590. VolData.Status = NEXT_FILE;
  3591. return FALSE;
  3592. }
  3593. // Get a handle to the file
  3594. if(!OpenNtfsFile()) {
  3595. VolData.Status = NEXT_FILE;
  3596. return FALSE;
  3597. }
  3598. }
  3599. // Partially defrag the file
  3600. return PartialDefrag();
  3601. }
  3602. /*****************************************************************************************************************
  3603. ROUTINE DESCRIPTION:
  3604. Send graphics to UI.
  3605. */
  3606. void SendGraphicsData()
  3607. {
  3608. char * pAnalyzeLineArray = NULL;
  3609. char * pDefragLineArray = NULL;
  3610. DISPLAY_DATA * pDispData = NULL;
  3611. __try {
  3612. // Kill the timer until we're done.
  3613. KillTimer(hwndMain, DISKVIEW_TIMER_ID);
  3614. // don't send the data unless the engine is running
  3615. if (VolData.EngineState != RUNNING){
  3616. return;
  3617. }
  3618. // if DiskView didn't get memory, forget it
  3619. if (!AnalyzeView.HasMapMemory() || !DefragView.HasMapMemory()) {
  3620. SendGraphicsMemoryErr();
  3621. return;
  3622. }
  3623. DISPLAY_DATA DisplayData = {0};
  3624. DWORD dwDispDataSize = 0;
  3625. // get copies of line arrays for analyze and defrag
  3626. // (delete copy when finished)
  3627. AnalyzeView.GetLineArray(&pAnalyzeLineArray, &DisplayData.dwAnalyzeNumLines);
  3628. DefragView.GetLineArray(&pDefragLineArray, &DisplayData.dwDefragNumLines);
  3629. // Allocate enough memory to hold both analyze and defrag displays.
  3630. // If only analyze or defrag is present, then the NumLines field for the
  3631. // other one will equal zero -- hence no additional allocation.
  3632. dwDispDataSize =
  3633. DisplayData.dwAnalyzeNumLines +
  3634. DisplayData.dwDefragNumLines +
  3635. sizeof(DISPLAY_DATA);
  3636. // If neither an analyze diskview nor a defrag diskview are present, don't continue.
  3637. if (DisplayData.dwAnalyzeNumLines == 0 && DisplayData.dwDefragNumLines == 0) {
  3638. return;
  3639. }
  3640. pDispData = (DISPLAY_DATA *) new char[dwDispDataSize];
  3641. // If we can't get memory, don't continue.
  3642. if (pDispData == NULL) {
  3643. return;
  3644. }
  3645. wcscpy(pDispData->cVolumeName, VolData.cVolumeName);
  3646. // Copy over the fields for the analyze and defrag data.
  3647. // If only one or the other is present, the fields for the other will equal zero.
  3648. pDispData->dwAnalyzeNumLines = DisplayData.dwAnalyzeNumLines;
  3649. pDispData->dwDefragNumLines = DisplayData.dwDefragNumLines;
  3650. // Get the line array for the analyze view if it exists.
  3651. if (pAnalyzeLineArray) {
  3652. CopyMemory((char*) &(pDispData->LineArray),
  3653. pAnalyzeLineArray,
  3654. DisplayData.dwAnalyzeNumLines);
  3655. }
  3656. // Get the line array for the defrag view if it exists
  3657. if (pDefragLineArray) {
  3658. CopyMemory((char*) ((BYTE*)&pDispData->LineArray) + DisplayData.dwAnalyzeNumLines,
  3659. pDefragLineArray,
  3660. DisplayData.dwDefragNumLines);
  3661. }
  3662. // If the gui is connected, send gui data to it
  3663. DataIoClientSetData(ID_DISP_DATA, (TCHAR*) pDispData, dwDispDataSize, pdataDfrgCtl);
  3664. Message(TEXT("engine sending graphics to UI"), -1, NULL);
  3665. }
  3666. __finally {
  3667. // clean up
  3668. if (pAnalyzeLineArray) {
  3669. delete [] pAnalyzeLineArray;
  3670. }
  3671. if (pDefragLineArray) {
  3672. delete [] pDefragLineArray;
  3673. }
  3674. if (pDispData) {
  3675. delete [] pDispData;
  3676. }
  3677. // reset the next timer for updating the disk view
  3678. if(SetTimer(hwndMain, DISKVIEW_TIMER_ID, DiskViewInterval, NULL) == 0)
  3679. {
  3680. LOG_ERR();
  3681. }
  3682. }
  3683. }
  3684. /*****************************************************************************************************************
  3685. ROUTINE DESCRIPTION:
  3686. This is the exit routine which will clean up all the open handles, free up all unused memory etc.
  3687. INPUT + OUTPUT:
  3688. None.
  3689. GLOBALS:
  3690. Pointless to enumerate here -- all unhandled handles (pun intended) and allocated memories are closed/freed.
  3691. RETURN:
  3692. None.
  3693. */
  3694. VOID
  3695. Exit(
  3696. )
  3697. {
  3698. // Delete the pointer to the GUI object.
  3699. ExitDataIoClient(&pdataDfrgCtl);
  3700. //If we were logging, then close the log file.
  3701. if(bLogFile){
  3702. ExitLogFile();
  3703. }
  3704. CoUninitialize();
  3705. //0.0E00 Close event logging.
  3706. CleanupLogging();
  3707. //Close the error log.
  3708. ExitErrorLog();
  3709. }
  3710. /*****************************************************************************************************************
  3711. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3712. ROUTINE DESCRIPTION:
  3713. Allocate a buffer for and read the MFT bitmap (bitmap attribute in
  3714. filerecord 0). The MFT bitmap contains one bit for each filerecord
  3715. in the MFT and has the bit set if the filerecord is in use and reset
  3716. if it is not in use.
  3717. INPUT + OUTPUT:
  3718. None.
  3719. GLOBALS:
  3720. IN VolData.hVolume - Handle to the volume
  3721. IN VolData.pFileRecord - Pointer to buffer to hold the file record for the current file.
  3722. IN VolData.BytesPerFRS - The number of bytes in a file record.
  3723. IN VolData.BytesPerSector - The number of bytes in a sector.
  3724. IN VolData.BytesPerCluster - The number of bytes in a cluster.
  3725. RETURN:
  3726. TRUE - Success.
  3727. FALSE - Fatal Error.
  3728. */
  3729. BOOL
  3730. GetMftBitmap(
  3731. )
  3732. {
  3733. EXTENT_LIST* pExtents = NULL;
  3734. LONGLONG Extent;
  3735. PUCHAR pMftBitmap = NULL;
  3736. PBYTE pByte;
  3737. LONGLONG Byte;
  3738. STREAM_EXTENT_HEADER* pStreamExtentHeader = NULL;
  3739. __try{
  3740. //0.0E00 Load the $MFT filerecord which is #0.
  3741. VolData.FileRecordNumber = 0;
  3742. EF(GetInUseFrs(VolData.hVolume, &VolData.FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  3743. //0.0E00 Error out if we didn't get the record.
  3744. EF_ASSERT(VolData.FileRecordNumber == 0);
  3745. //0.0E00 Build an extent list of the MFT bitmap (which is stored in the $BITMAP attribute).
  3746. EF(GetStreamExtentsByNameAndType(TEXT(""), $BITMAP, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord));
  3747. //0.0E00 Allocate a buffer large enough to hold the MFT bitmap per the extent data gotten in GetExtentList.
  3748. EF(AllocateMemory((ULONG)((VolData.NumberOfClusters * VolData.BytesPerCluster) + VolData.BytesPerCluster), &VolData.hMftBitmap, (void**)&pMftBitmap));
  3749. //0.0E00 Save a pointer to the beginning of the buffer.
  3750. pByte = (PBYTE)pMftBitmap;
  3751. //Get a pointer to the stream header.
  3752. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  3753. //Get a pointer to the extent list.
  3754. pExtents = (EXTENT_LIST*)((UCHAR*)VolData.pExtentList + sizeof(STREAM_EXTENT_HEADER));
  3755. //0.0E00 Loop through each extent for the bitmap.
  3756. for(Extent = 0; Extent < pStreamExtentHeader->ExtentCount; Extent ++){
  3757. //0.0E00 Read the data in that extent into the buffer.
  3758. EF(DasdReadClusters(VolData.hVolume,
  3759. pExtents[Extent].StartingLcn,
  3760. pExtents[Extent].ClusterCount,
  3761. pMftBitmap,
  3762. VolData.BytesPerSector,
  3763. VolData.BytesPerCluster));
  3764. //0.0E00 Update our pointer to the end of the bitmap.
  3765. pMftBitmap += (pExtents[Extent].ClusterCount * VolData.BytesPerCluster);
  3766. }
  3767. //0.0E00 Count how many file records are in use.
  3768. for(Byte = 0; Byte < VolData.TotalFileRecords / 8; Byte ++){
  3769. //0.0E00 Use the bit array above to determine how many bits are set in this byte, and add it to the total.
  3770. VolData.InUseFileRecords += CountBitsArray[pByte[Byte]];
  3771. }
  3772. }
  3773. __finally{
  3774. //0.0E00 Cleanup.
  3775. if((pMftBitmap != NULL) && (VolData.hMftBitmap != NULL)){
  3776. GlobalUnlock(VolData.hMftBitmap);
  3777. }
  3778. }
  3779. return TRUE;
  3780. }
  3781. /*****************************************************************************************************************
  3782. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3783. ROUTINE DESCRIPTION:
  3784. Get the name of the pagefiles and store them in a double null-terminated list of null terminated strings.
  3785. INPUT + OUTPUT:
  3786. IN cDrive - The current drive so that this can tell which pagefile names to store. (Only the current drive.)
  3787. OUT phPageFileNames - Where to store the handle for the allocated memory.
  3788. OUT ppPagseFileNames - Where to store the pointer for the pagefile names.
  3789. GLOBALS:
  3790. None.
  3791. RETURN:
  3792. TRUE - Success.
  3793. FALSE - Fatal Error.
  3794. */
  3795. BOOL
  3796. GetPagefileNames(
  3797. IN TCHAR cDrive,
  3798. OUT HANDLE * phPageFileNames,
  3799. OUT TCHAR ** ppPageFileNames
  3800. )
  3801. {
  3802. HKEY hKey = NULL;
  3803. ULONG lRegLen = 0;
  3804. int i;
  3805. int iStrLen;
  3806. int iNameStart = 0;
  3807. TCHAR * pTemp;
  3808. TCHAR * pProcessed;
  3809. DWORD dwRet = 0;
  3810. DWORD dwType = 0;
  3811. //0.0E00 Open the registry key to the pagefile.
  3812. EF_ASSERT(ERROR_SUCCESS == RegOpenKeyEx(
  3813. HKEY_LOCAL_MACHINE,
  3814. TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management"),
  3815. 0,
  3816. KEY_QUERY_VALUE,
  3817. &hKey));
  3818. //0.0E00 Find out how much memory we need to hold the value pagefile names.
  3819. EF_ASSERT(ERROR_SUCCESS == RegQueryValueEx(
  3820. hKey,
  3821. TEXT("PagingFiles"),
  3822. 0,
  3823. &dwType,
  3824. NULL,
  3825. &lRegLen));
  3826. //0.0E00 If there is no data then allocate enough for two bytes (a double termination).
  3827. if(lRegLen<2){
  3828. lRegLen = 2;
  3829. }
  3830. //0.0E00 Allocate enough memory.
  3831. EF(AllocateMemory(lRegLen, phPageFileNames, (void**)ppPageFileNames));
  3832. //0.0E00 Get the value.
  3833. EF_ASSERT(ERROR_SUCCESS ==RegQueryValueEx(
  3834. hKey,
  3835. TEXT("PagingFiles"),
  3836. 0,
  3837. &dwType,
  3838. (LPBYTE)*ppPageFileNames,
  3839. &lRegLen));
  3840. //0.0E00 Strip out the numbers and drive letters so that we have only the pagefile names.
  3841. // The REG_MULTI_SZ type has a series of null terminated strings with a double null termination at the end
  3842. // of the list.
  3843. // The format of each string is "c:\pagefile.sys 100 100". The data after the slash and before the first space
  3844. // is the page file name. The numbers specify the size of the pagefile which we don't care about.
  3845. // We extract the filename minus the drive letter of the pagefile (which must be in the root dir so we don't
  3846. // need to worry about subdirs existing). Therfore we put a null at the first space, and shift the pagefile
  3847. // name earlier so that we don't have c:\ in there. The end product should be a list of pagefile
  3848. // names with a double null termination for example: "pagefile.sys[null]pagefile2.sys[null][null]" Furthermore,
  3849. // we only take names for this drive, so the string may simply consist of a double null termination.
  3850. // We use the same memory space for output as we use for input, so we just clip the pagefile.sys and bump it up
  3851. // to the beginning of ppPageFileNames. We keep a separate pointer which points to the next byte after
  3852. // The previous outputed data.
  3853. pProcessed = pTemp = *ppPageFileNames;
  3854. //0.0E00 For each string...
  3855. while(*pTemp!=0){
  3856. iStrLen = lstrlen(pTemp);
  3857. //0.0E00 If this pagefile is on the current drive.
  3858. if((TCHAR)CharUpper((TCHAR*)pTemp[0]) == (TCHAR)CharUpper((TCHAR*)cDrive)){
  3859. //0.0E00 Go through each character in this string.
  3860. for(i=0; i<iStrLen; i++){
  3861. //0.0E00 If this is a slash, then the next character is the first of the pagefile name.
  3862. if(pTemp[i] == TEXT('\\')){
  3863. iNameStart = i+1;
  3864. continue;
  3865. }
  3866. //0.0E00 If this is a space then the rest of the string is numbers. Null terminate it here.
  3867. if(pTemp[i] == TEXT(' ')){
  3868. pTemp[i] = 0;
  3869. break;
  3870. }
  3871. }
  3872. //0.0E00 Bump the string up so all the processed names are adjacent.
  3873. MoveMemory(pProcessed, pTemp+iNameStart, (lstrlen(pTemp+iNameStart)+1)*sizeof(TCHAR));
  3874. //0.0E00 Note where the next string should go.
  3875. pProcessed += lstrlen(pProcessed) + 1;
  3876. VolData.NumPagefiles++;
  3877. }
  3878. //0.0E00 If this pagefile is not on this current drive then simply ignore it.
  3879. else{
  3880. }
  3881. //0.0E00 Note where to search for the next string.
  3882. pTemp += iStrLen + 1;
  3883. }
  3884. //0.0E00 Add double null termination.
  3885. *pProcessed = 0;
  3886. EF_ASSERT(RegCloseKey(hKey)==ERROR_SUCCESS);
  3887. return TRUE;
  3888. }
  3889. /*****************************************************************************************************************
  3890. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3891. ROUTINE DESCRIPTION:
  3892. Check to see if a file is a pagefile and grab it's extent list if it is.
  3893. INPUT + OUTPUT:
  3894. IN FileRecordNumber - The number of the filerecord we want to check.
  3895. IN pFileRecord - A pointer to the filerecord for the file we want to check.
  3896. GLOBALS:
  3897. OUT VolData.PagefileFrags - The number of fragments in the pagefile if it is a pagefile.
  3898. OUT VolData.PagefileSize - The number of bytes in the pagefile if it is a pagefile.
  3899. IN OUT Various other VolData fields.
  3900. RETURN:
  3901. TRUE - Success.
  3902. FALSE - Fatal Error.
  3903. */
  3904. BOOL
  3905. CheckForPagefileNtfs(
  3906. IN LONGLONG FileRecordNumber,
  3907. IN FILE_RECORD_SEGMENT_HEADER* pFileRecord
  3908. )
  3909. {
  3910. //0.0E00 Get file's name
  3911. LONGLONG ParentFileRecordNumber;
  3912. TCHAR cFileName[MAX_PATH+1];
  3913. EF(GetNameFromFileRecord(pFileRecord,
  3914. cFileName,
  3915. &ParentFileRecordNumber));
  3916. VolData.vFileName = cFileName;
  3917. if (!VolData.vFileName.GetBuffer()) {
  3918. return FALSE;
  3919. }
  3920. //0.0E00 Check if this pagefile is in the root dir.
  3921. if(ParentFileRecordNumber != ROOT_FILE_NAME_INDEX_NUMBER){
  3922. //0.0E00 No it isn't so just return.
  3923. return TRUE;
  3924. }
  3925. //0.0E00 See if this is a pagefile
  3926. if(!CheckPagefileNameMatch(VolData.vFileName.GetBuffer(), pPageFileNames)){
  3927. //0.0E00 No it isn't so just return.
  3928. return TRUE;
  3929. }
  3930. //0.0E00 Get the pagefile's extent list
  3931. VolData.pFileRecord = pFileRecord;
  3932. VolData.FileRecordNumber = FileRecordNumber;
  3933. EF(GetExtentList(DEFAULT_STREAMS, NULL));
  3934. //Get a pointer to the extent list's file header.
  3935. FILE_EXTENT_HEADER* pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  3936. //0.0E00 If this file is compressed then it is not a valid pagefile so just return.
  3937. if(VolData.bCompressed){
  3938. return TRUE;
  3939. }
  3940. //0.0E00 Set the pagefile's stats and return.
  3941. VolData.bPageFile = TRUE;
  3942. // in case there is more than 1 pagefile, you need to add them up
  3943. VolData.PagefileFrags += pFileExtentHeader->ExcessExtents + 1;
  3944. VolData.PagefileSize += VolData.FileSize;
  3945. return TRUE;
  3946. }
  3947. /*****************************************************************************************************************
  3948. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3949. ROUTINE DESCRIPTION:
  3950. Check a name against all the pagefile names to see if this name matches that of a pagefile.
  3951. INPUT + OUTPUT:
  3952. IN pCompareName - The name of that we are checking to see if it is a pagefile.
  3953. IN pPageFileNames - The list of pagefile names for this drive.
  3954. GLOBALS:
  3955. None.
  3956. RETURN:
  3957. TRUE - This name matches a pagefile name.
  3958. FALSE - This name does not match a pagefile name.
  3959. */
  3960. BOOL
  3961. CheckPagefileNameMatch(
  3962. IN TCHAR * pCompareName,
  3963. IN TCHAR * pPageFileNames
  3964. )
  3965. {
  3966. if (!pCompareName || !pPageFileNames) {
  3967. return FALSE;
  3968. }
  3969. //0.0E00 Loop through all the pagefile names -- the list is double null terminated.
  3970. while(*pPageFileNames!=0){
  3971. //0.0E00 Check if these names match.
  3972. if(!lstrcmpi(pCompareName, pPageFileNames)){
  3973. return TRUE;
  3974. }
  3975. //0.0E00 If not then move to the next name.
  3976. else{
  3977. pPageFileNames+=lstrlen(pPageFileNames)+1;
  3978. }
  3979. }
  3980. //0.0E00 No match with any of the names, so return FALSE.
  3981. return FALSE;
  3982. }
  3983. /*****************************************************************************************************************
  3984. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  3985. ROUTINE DESCRIPTION:
  3986. Prints out the disk statistics on screen.
  3987. INPUT + OUTPUT:
  3988. None.
  3989. GLOBALS:
  3990. IN various VolData fields that get printed onto the screen.
  3991. RETURN:
  3992. None.
  3993. */
  3994. VOID
  3995. DisplayNtfsVolumeStats()
  3996. {
  3997. ULONG iTmp;
  3998. LONGLONG llTmp;
  3999. Trace(log, " * NTFS Volume Statistics:");
  4000. Trace(log, " Total sectors on disk = %I64d", VolData.TotalSectors);
  4001. Trace(log, " Bytes per sector = %I64d", VolData.BytesPerSector);
  4002. Trace(log, " Bytes per cluster = %I64d", VolData.BytesPerCluster);
  4003. Trace(log, " Sectors per cluster = %I64d", VolData.SectorsPerCluster);
  4004. Trace(log, " Total clusters on disk = %I64d", VolData.TotalClusters);
  4005. Trace(log, " Bytes per File Record Segment = %I64d", VolData.BytesPerFRS);
  4006. Trace(log, " Volume Bitmap Size = %I64d", VolData.BitmapSize);
  4007. Trace(log, " NumberOfFileRecords = %I64d", VolData.TotalFileRecords);
  4008. Trace(log, " Mft Start Lcn = 0x%I64x (%I64d)", VolData.MftStartLcn, VolData.MftStartLcn);
  4009. Trace(log, " Mft2 Start Lcn = 0x%I64x (%I64d)", VolData.Mft2StartLcn, VolData.Mft2StartLcn);
  4010. Trace(log, " Mft Zone Start = 0x%I64x (%I64d)", VolData.MftZoneStart, VolData.MftZoneStart);
  4011. Trace(log, " Mft Zone End = 0x%I64x (%I64d)", VolData.MftZoneEnd, VolData.MftZoneEnd);
  4012. llTmp = VolData.MftZoneEnd - VolData.MftZoneStart;
  4013. Trace(log, " Mft Zone Size = %I64d clusters", llTmp);
  4014. Trace(log, " Mft Start Offset = 0x%I64x (%I64d)", VolData.MftStartOffset, VolData.MftStartOffset);
  4015. Trace(log, " Disk Size = %I64d", VolData.TotalClusters * VolData.BytesPerCluster);
  4016. Trace(log, " Cluster Size = %I64d", VolData.BytesPerCluster);
  4017. Trace(log, " Used Space = %I64d bytes", VolData.UsedClusters * VolData.BytesPerCluster);
  4018. Trace(log, " Free Space = %I64d bytes", (VolData.TotalClusters - VolData.UsedClusters) * VolData.BytesPerCluster);
  4019. Trace(log, " Free Space = %I64d bytes", VolData.FreeSpace);
  4020. Trace(log, " Usable Free Space = %I64d bytes", VolData.UsableFreeSpace);
  4021. Trace(log, " Smallest Free Space = %I64d clusters", VolData.SmallestFreeSpaceClusters);
  4022. Trace(log, " Largest Free Space = %I64d clusters", VolData.LargestFreeSpaceClusters);
  4023. Trace(log, " Average Free Space = %I64d clusters", VolData.AveFreeSpaceClusters);
  4024. Trace(log, " Pagefile Size = %I64d", VolData.PagefileSize);
  4025. Trace(log, " Pagefile Fragments = %I64d", VolData.PagefileFrags);
  4026. Trace(log, " Number of Active Pagefiles on this Drive = %I64d", VolData.NumPagefiles);
  4027. Trace(log, " Total Directories = %I64d", VolData.TotalDirs);
  4028. Trace(log, " Fragmented Dirs = %I64d", VolData.NumFraggedDirs);
  4029. Trace(log, " Excess Dir Frags = %I64d", VolData.NumExcessDirFrags);
  4030. Trace(log, " Total Files = %I64d", VolData.TotalFiles);
  4031. Trace(log, " Current File = %I64d", VolData.CurrentFile);
  4032. Trace(log, " Total File Space = %I64d bytes", VolData.TotalFileSpace);
  4033. Trace(log, " Total File Bytes = %I64d bytes", VolData.TotalFileBytes);
  4034. Trace(log, " Avg. File Size = %I64d bytes", VolData.AveFileSize);
  4035. Trace(log, " Fragmented Files = %I64d", VolData.NumFraggedFiles);
  4036. Trace(log, " Excess Fragments = %I64d", VolData.NumExcessFrags);
  4037. if (VolData.TotalClusters - VolData.UsedClusters){
  4038. iTmp = (ULONG)(100 * (VolData.NumFreeSpaces - 4) /
  4039. (VolData.TotalClusters - VolData.UsedClusters));
  4040. }
  4041. else {
  4042. iTmp = -1;
  4043. }
  4044. Trace(log, " Free Space Fragmention Percent = %ld", iTmp);
  4045. Trace(log, " Fragged Space = %I64d bytes", VolData.FraggedSpace);
  4046. Trace(log, " File Fragmention Percent = %I64d", VolData.PercentDiskFragged);
  4047. Trace(log, " MFT size = %I64d kb", VolData.MftSize / 1024);
  4048. Trace(log, " # MFT records = %I64d", VolData.TotalFileRecords);
  4049. if(VolData.TotalFileRecords != 0) {
  4050. iTmp = (ULONG)(100 * VolData.InUseFileRecords / VolData.TotalFileRecords);
  4051. }
  4052. else {
  4053. iTmp = -1;
  4054. }
  4055. Trace(log, " Percent MFT in use = %ld", iTmp);
  4056. Trace(log, " MFT Fragments = %I64d", VolData.MftNumberOfExtents);
  4057. // time data
  4058. Trace(log, " Start Time = %s", GetTmpTimeString(VolData.StartTime));
  4059. Trace(log, " End Time = %s", GetTmpTimeString(VolData.EndTime));
  4060. DWORD dwSeconds;
  4061. if (GetDeltaTime(&VolData.StartTime, &VolData.EndTime, &dwSeconds)){
  4062. Trace(log, " Delta Time = %d seconds", dwSeconds);
  4063. }
  4064. Trace(log, " * End of Statistics");
  4065. }
  4066. /*****************************************************************************************************************
  4067. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4068. ROUTINE DESCRIPTION:
  4069. Displays data about the current file for the developer.
  4070. INPUT + OUTPUT:
  4071. None.
  4072. GLOBALS:
  4073. IN Various VolData fields.
  4074. RETURN:
  4075. None.
  4076. */
  4077. VOID
  4078. DisplayNtfsFileSpecsFunction(
  4079. )
  4080. {
  4081. TCHAR cString[300];
  4082. //0.0E00 Display File Name, number of extents and number of fragments.
  4083. _stprintf(cString, TEXT("Extents = 0x%lX "), ((FILE_EXTENT_HEADER*)VolData.pExtentList)->ExcessExtents+((FILE_EXTENT_HEADER*)VolData.pExtentList)->NumberOfStreams);
  4084. Message(cString, -1, NULL);
  4085. _stprintf(cString,
  4086. TEXT("%s %s at Lcn 0x%lX for Cluster Count of 0x%lX"),
  4087. (VolData.bFragmented == TRUE) ? TEXT("Fragmented") : TEXT("Contiguous"),
  4088. (VolData.bDirectory) ? TEXT("Directory") : TEXT("File"),
  4089. (ULONG)VolData.StartingLcn,
  4090. (ULONG)VolData.NumberOfClusters);
  4091. Message(cString, -1, NULL);
  4092. }
  4093. /*****************************************************************************************************************
  4094. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4095. ROUTINE DESCRIPTION:
  4096. Load a requested file record into a memory buffer. If the file record is not in use then
  4097. load the next in-use file record and return its file record number.
  4098. Note:
  4099. GetFrs reads MFT_BUFFER_SIZE of the MFT at a time and returns pointers
  4100. to the filerecords within the buffer rather than loading each filerecord individually.
  4101. INPUT + OUTPUT:
  4102. IN OUT pFileRecordNumber - Points to the file record number we're supposed to read, and where we write back
  4103. which number we actually read.
  4104. IN pMftExtentList - The list of all the extents for the MFT (so we can DASD read the MFT).
  4105. IN pMftBitmap - The MFT bitmap (so we can see if records are in use.
  4106. IN pMftBuffer - The buffer that we hold a chunk of the MFT in.
  4107. OUT pFileRecord - A pointer to a pointer so we can pass back the file record requested.
  4108. GLOBALS:
  4109. IN OUT Various VolData fields.
  4110. RETURN:
  4111. TRUE - Success.
  4112. FALSE - Fatal Error.
  4113. */
  4114. BOOL
  4115. GetFrs(
  4116. IN LONGLONG* pFileRecordNumber,
  4117. IN EXTENT_LIST* pMftExtentList,
  4118. IN UCHAR* pMftBitmap,
  4119. IN FILE_RECORD_SEGMENT_HEADER* pMftBuffer,
  4120. OUT FILE_RECORD_SEGMENT_HEADER* pFileRecord
  4121. )
  4122. {
  4123. LONGLONG FileRecordNumber = *pFileRecordNumber;
  4124. LONGLONG Extent;
  4125. LONGLONG Cluster;
  4126. LONGLONG Vcn;
  4127. LONGLONG ClustersInMftBuffer;
  4128. PUCHAR pUchar = (PUCHAR)pMftBuffer;
  4129. LONGLONG i;
  4130. LONGLONG FileRecordsPerCluster;
  4131. LONGLONG ClustersXfered;
  4132. //Initialize FileRecordsPerCluster just in case we don't have to compute it...
  4133. FileRecordsPerCluster = 1;
  4134. //0.0E00 Loop until we find a bit that is in use in the bitmap (1 means in use, 0 means not in use).
  4135. //If the current file record is not in use then we'll find the next that is.
  4136. //FileRecordNumber/8 gets us the byte that contains the bit for this file record.
  4137. //FileRecordNumber&7 gets us the bit offset in the byte for our record.
  4138. while((pMftBitmap[FileRecordNumber / 8] & (1 << (FileRecordNumber & 7))) == 0){
  4139. //0.0E00 Since this record is not in use, increment our offset.
  4140. FileRecordNumber ++;
  4141. //0.0E00 If we've gone beyond the end if the bitmap...
  4142. if(FileRecordNumber >= VolData.TotalFileRecords){
  4143. //0.0E00 Note this file record number and return.
  4144. *pFileRecordNumber = FileRecordNumber;
  4145. return TRUE;
  4146. }
  4147. }
  4148. //0.0E00 We got here so we found an in use file record.
  4149. //0.0E00 Return the number of this record.
  4150. *pFileRecordNumber = FileRecordNumber;
  4151. //0.0E00 If this filerecord isn't already in the buffer, load the block that contains it.
  4152. //0.0E00 FileRecordLow keeps track of the lowest filerecord number currently loaded, FileRecordHi the highest.
  4153. if((FileRecordNumber < FileRecordLow) || (FileRecordNumber >= FileRecordHi)){
  4154. //0.0E00 If there are 1 or more clusters per FRS...
  4155. if(VolData.ClustersPerFRS){
  4156. //0.0E00 Note which cluster offset this file record number is in the MFT.
  4157. Cluster = FileRecordNumber * VolData.ClustersPerFRS;
  4158. }
  4159. //0.0E00 ...else there are multiple file records per cluster.
  4160. else{
  4161. //0.0E00 Error if there are no bytes in an FRS.
  4162. EF_ASSERT(VolData.BytesPerFRS != 0);
  4163. //0.0E00 Calculate how many file records there are in a cluster.
  4164. FileRecordsPerCluster = VolData.BytesPerCluster / VolData.BytesPerFRS;
  4165. //0.0E00 Error if there are no file records per cluster.
  4166. EF_ASSERT(FileRecordsPerCluster != 0);
  4167. //0.0E00 Note which cluster offset this file record number is into the MFT.
  4168. Cluster = FileRecordNumber / FileRecordsPerCluster;
  4169. }
  4170. //0.0E00 Find the extent that the filerecord's first cluster is in
  4171. //0.0E00 Loop through each extent of the MFT starting with the first extent.
  4172. for(Vcn = 0, Extent = 0; Extent < VolData.MftNumberOfExtents; Extent ++){
  4173. //0.0E00 If the cluster we are looking for fits between the first cluster of this extent (Vcn) and the last, then we have found our extent.
  4174. if((Vcn <= Cluster) && (Cluster < (Vcn + pMftExtentList[Extent].ClusterCount))){
  4175. break;
  4176. }
  4177. //0.0E00 Otherwise we haven't found our extent so update to the next extent.
  4178. Vcn += pMftExtentList[Extent].ClusterCount;
  4179. }
  4180. //0.0E00 Figure out how many clusters will fit in our buffer.
  4181. EF_ASSERT(VolData.BytesPerCluster != 0);
  4182. ClustersInMftBuffer = MFT_BUFFER_SIZE / VolData.BytesPerCluster;
  4183. //0.0E00 If all the clusters for this block are in this extent, load them in one read
  4184. if((Cluster + ClustersInMftBuffer) <= (Vcn + pMftExtentList[Extent].ClusterCount)){
  4185. //0.0E00 Read one buffer full.
  4186. EF_ASSERT(VolData.BytesPerCluster != 0);
  4187. EF(DasdReadClusters(VolData.hVolume,
  4188. pMftExtentList[Extent].StartingLcn + (Cluster - Vcn),
  4189. MFT_BUFFER_SIZE / VolData.BytesPerCluster,
  4190. pMftBuffer,
  4191. VolData.BytesPerSector,
  4192. VolData.BytesPerCluster));
  4193. ClustersXfered = ClustersInMftBuffer;
  4194. }
  4195. //0.0E00 If all the clusters for this block do not fit in this extent, do as many reads as necessary to fill up the buffer.
  4196. else{
  4197. //Keep track of how many clusters we actually transferred
  4198. ClustersXfered = 0;
  4199. //0.0E00 Cluster will be an offset from the beginning of the extent rather than from the beginning of the MFT.
  4200. Cluster -= Vcn;
  4201. //0.0E00 Keep reading extents until we fill the buffer.
  4202. for(i = 0; i < ClustersInMftBuffer; i ++){
  4203. //0.0E00 Read one cluster.
  4204. EF(DasdReadClusters(VolData.hVolume,
  4205. pMftExtentList[Extent].StartingLcn + Cluster,
  4206. 1,
  4207. pUchar,
  4208. VolData.BytesPerSector,
  4209. VolData.BytesPerCluster));
  4210. ClustersXfered++;
  4211. //0.0E00 Read the next cluster.
  4212. Cluster ++;
  4213. //0.0E00 If this cluster is beyond the end of the extent, start on the next extent.
  4214. if(Cluster >= pMftExtentList[Extent].ClusterCount){
  4215. Extent ++;
  4216. if(Extent >= VolData.MftNumberOfExtents){
  4217. break;
  4218. }
  4219. Cluster = 0;
  4220. }
  4221. //0.0E00 Keep our pointer into our buffer pointing to the end.
  4222. pUchar += VolData.BytesPerCluster;
  4223. }
  4224. }
  4225. //0.0E00 Record the first and last filerecord numbers now in the buffer
  4226. EF_ASSERT(VolData.BytesPerFRS != 0);
  4227. //0.0E00 Record the first and last file record numbers now in the buffer
  4228. FileRecordLow = FileRecordNumber & ~(FileRecordsPerCluster-1);
  4229. if (VolData.ClustersPerFRS) {
  4230. FileRecordHi = FileRecordLow + (ClustersXfered / VolData.ClustersPerFRS);
  4231. }
  4232. else {
  4233. FileRecordHi = FileRecordLow + (ClustersXfered * FileRecordsPerCluster);
  4234. }
  4235. }
  4236. //0.0E00 Return a pointer to the filerecord in the buffer
  4237. CopyMemory(pFileRecord,
  4238. (PFILE_RECORD_SEGMENT_HEADER)((PUCHAR)(pMftBuffer) + ((FileRecordNumber - FileRecordLow) * VolData.BytesPerFRS)),
  4239. (ULONG)VolData.BytesPerFRS);
  4240. //
  4241. // Check to make sure that the file record has the "FILE" signature. If it doesn't,
  4242. // the FRS is corrupt.
  4243. //
  4244. if ((pFileRecord->MultiSectorHeader.Signature[0] != 'F') ||
  4245. (pFileRecord->MultiSectorHeader.Signature[1] != 'I') ||
  4246. (pFileRecord->MultiSectorHeader.Signature[2] != 'L') ||
  4247. (pFileRecord->MultiSectorHeader.Signature[3] != 'E')
  4248. ) {
  4249. //
  4250. // This FRS is corrupt.
  4251. //
  4252. VolData.bMFTCorrupt = TRUE;
  4253. return FALSE;
  4254. }
  4255. //0.0E00 Make the USA adjustments for this filerecord.
  4256. EF(AdjustUSA(pFileRecord));
  4257. return TRUE;
  4258. }
  4259. /*****************************************************************************************************************
  4260. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4261. ROUTINE DESCRIPTION:
  4262. Each filerecord has a verification encoding called Update Sequence Array.
  4263. Throughout the file record at even intervals (every 256 bytes), two bytes are replaced with
  4264. data for verification of the file record by the file system. In order that the file record
  4265. can be reconstructed for use again, there is an array near the beginning of the file record
  4266. that contains an array of bytes holding each of the replaced bytes. Thus, you can extract
  4267. the bytes from the array, put them back throughout the file record and it is usable again.
  4268. The number that is used to replace the bytes at even intervals is the same throughout each
  4269. file record. This number is stored in the first two bytes of the Update Sequence Array.
  4270. Therefore, the verification that the file system normally does before the file record is
  4271. reconstructed is to compare these first two bytes with each two replaced bytes throughout
  4272. the record to make sure they are equal. The file record is then reconstructed.
  4273. Normally the USA (Update Sequence Array) is handled by the file system, but since we are
  4274. reading directly from the disk with DASD reads, we must do it ourselves.
  4275. AdjustUSA decodes the filerecord pointed to by pFrs.
  4276. INPUT + OUTPUT:
  4277. IN OUT pFrs - A pointer to file record to decode.
  4278. GLOBALS:
  4279. None.
  4280. RETURN:
  4281. TRUE - Success.
  4282. FALSE - Fatal Error.
  4283. */
  4284. BOOL
  4285. AdjustUSA(
  4286. IN OUT FILE_RECORD_SEGMENT_HEADER* pFrs
  4287. )
  4288. {
  4289. PUSHORT pUsa;
  4290. PUSHORT pUshort;
  4291. USHORT UsaLength;
  4292. USHORT Usn;
  4293. LONGLONG i;
  4294. //0.0E00 Get a pointer to the Update Sequence Array
  4295. pUsa = (PUSHORT)((PUCHAR)pFrs+pFrs->MultiSectorHeader.UpdateSequenceArrayOffset);
  4296. //bug #9914 AV on initization if multi volume disk failed
  4297. //need to check if pUsa is not NULL
  4298. if(pUsa == NULL)
  4299. {
  4300. return FALSE;
  4301. }
  4302. //0.0E00 Get the length of the array
  4303. UsaLength = pFrs->MultiSectorHeader.UpdateSequenceArraySize;
  4304. //0.0E00 Get a 2 byte array pointer to the file record
  4305. pUshort = (PUSHORT)pFrs;
  4306. // 0.0E00 Get the first number from the array (called the Update Sequence Number).
  4307. Usn = *pUsa++;
  4308. //0.0E00 Loop thru the file record
  4309. for(i=1; i<UsaLength; i++){
  4310. //0.0E00 Error if the Update Sequence entry doesn't match the USN
  4311. EF(pUshort[(i*256)-1] == Usn);
  4312. //0.0E00 Replace the Update Sequence Entry from the array and move to the next array entry
  4313. pUshort[(i*256)-1] = *pUsa++;
  4314. }
  4315. return TRUE;
  4316. }
  4317. /****************************************************************************************************************
  4318. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4319. ROUTINE DESCRIPTION:
  4320. Deallocates the mapping files for the file lists.
  4321. INPUT + OUTPUT:
  4322. None.
  4323. GLOBALS:
  4324. Similar to AllocateFileLists above.
  4325. RETURN:
  4326. TRUE - Success.
  4327. FALSE - Fatal Error.
  4328. */
  4329. BOOL
  4330. DeallocateFileLists(
  4331. )
  4332. {
  4333. TCHAR cString[300];
  4334. PVOID pTableContext = NULL;
  4335. if(VolData.hSysList){
  4336. EH_ASSERT(GlobalUnlock(VolData.hSysList) == FALSE);
  4337. EH_ASSERT(GlobalFree(VolData.hSysList) == NULL);
  4338. VolData.hSysList = NULL;
  4339. VolData.pSysList = NULL;
  4340. }
  4341. VolData.pFreeSpaceEntry = NULL;
  4342. VolData.pFileListEntry = NULL;
  4343. //
  4344. // Reinitialize the tables, so they're zero'ed out
  4345. //
  4346. pTableContext = NULL;
  4347. RtlInitializeGenericTable(&VolData.FragmentedFileTable,
  4348. FileEntryStartLcnCompareRoutine,
  4349. FileEntryAllocateRoutine,
  4350. FileEntryFreeRoutine,
  4351. pTableContext);
  4352. pTableContext = NULL;
  4353. RtlInitializeGenericTable(&VolData.ContiguousFileTable,
  4354. FileEntryStartLcnCompareRoutine,
  4355. FileEntryAllocateRoutine,
  4356. FileEntryFreeRoutine,
  4357. pTableContext);
  4358. pTableContext = NULL;
  4359. RtlInitializeGenericTable(&VolData.NonMovableFileTable,
  4360. FileEntryStartLcnCompareRoutine,
  4361. FileEntryAllocateRoutine,
  4362. FileEntryFreeRoutine,
  4363. pTableContext);
  4364. if(VolData.hNameList){
  4365. EH_ASSERT(GlobalUnlock(VolData.hNameList) == FALSE);
  4366. EH_ASSERT(GlobalFree(VolData.hNameList) == NULL);
  4367. VolData.hNameList = NULL;
  4368. VolData.pNameList = NULL;
  4369. }
  4370. //
  4371. // Free the memory allocated for the tables
  4372. //
  4373. SaFreeContext(&VolData.SaFileEntryContext);
  4374. ClearFreeSpaceTable();
  4375. /* _stprintf(cString, TEXT("Shared memory freed for Drive %ws"), VolData.cDisplayLabel);
  4376. Message(cString, -1, NULL);
  4377. */
  4378. return TRUE;
  4379. }
  4380. /****************************************************************************************************************
  4381. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4382. ROUTINE DESCRIPTION:
  4383. INPUT + OUTPUT:
  4384. GLOBALS:
  4385. RETURN:
  4386. TRUE - Success.
  4387. FALSE - Fatal error.
  4388. */
  4389. BOOL
  4390. SendMostFraggedList(
  4391. IN CONST BOOL fAnalyseOnly
  4392. )
  4393. {
  4394. CFraggedFileList fraggedFileList(VolData.cVolumeName);
  4395. // Build the most fragged list
  4396. EF(FillMostFraggedList(fraggedFileList, fAnalyseOnly));
  4397. // create the block of data to send to UI
  4398. EF(fraggedFileList.CreateTransferBuffer());
  4399. // Send the packet to the UI.
  4400. DataIoClientSetData(
  4401. ID_FRAGGED_DATA,
  4402. fraggedFileList.GetTransferBuffer(),
  4403. fraggedFileList.GetTransferBufferSize(),
  4404. pdataDfrgCtl);
  4405. return TRUE;
  4406. }
  4407. /*****************************************************************************************************************
  4408. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4409. ROUTINE DESCRIPTION:
  4410. INPUT + OUTPUT:
  4411. RETURN:
  4412. None.
  4413. */
  4414. VOID
  4415. SendReportData(
  4416. )
  4417. {
  4418. TEXT_DATA textData = {0};
  4419. _tcscpy(textData.cVolumeName, VolData.cVolumeName);
  4420. _tcscpy(textData.cVolumeLabel, VolData.cVolumeLabel);
  4421. _tcscpy(textData.cFileSystem, TEXT("NTFS"));
  4422. //Figure out how many free spaces there are on the drive.
  4423. CountFreeSpaces();
  4424. // get usable free space
  4425. LONGLONG llUsableFreeClusters;
  4426. if (DetermineUsableFreespace(&llUsableFreeClusters)){
  4427. VolData.UsableFreeSpace = llUsableFreeClusters * VolData.BytesPerCluster;
  4428. }
  4429. else{
  4430. VolData.UsableFreeSpace = VolData.FreeSpace;
  4431. }
  4432. //Fill in all the TEXT_DATA fields for the UI's text display.
  4433. textData.DiskSize = VolData.TotalClusters * VolData.BytesPerCluster;
  4434. textData.BytesPerCluster = VolData.BytesPerCluster;
  4435. textData.UsedSpace = VolData.UsedClusters * VolData.BytesPerCluster;
  4436. textData.FreeSpace = (VolData.TotalClusters - VolData.UsedClusters) *
  4437. VolData.BytesPerCluster;
  4438. EV_ASSERT(VolData.TotalClusters);
  4439. textData.FreeSpacePercent = 100 * (VolData.TotalClusters - VolData.UsedClusters) /
  4440. VolData.TotalClusters;
  4441. textData.UsableFreeSpace = VolData.UsableFreeSpace;
  4442. textData.UsableFreeSpacePercent = 100 * VolData.UsableFreeSpace /
  4443. (VolData.TotalClusters * VolData.BytesPerCluster);
  4444. textData.PagefileBytes = VolData.PagefileSize;
  4445. textData.PagefileFrags = __max(VolData.PagefileFrags, 0);
  4446. textData.TotalDirectories = __max(VolData.TotalDirs, 1);
  4447. textData.FragmentedDirectories = __max(VolData.NumFraggedDirs, 1);
  4448. textData.ExcessDirFrags = __max(VolData.NumExcessDirFrags, 0);
  4449. textData.TotalFiles = VolData.TotalFiles;
  4450. textData.AvgFileSize = VolData.AveFileSize;
  4451. textData.NumFraggedFiles = __max(VolData.NumFraggedFiles, 0);
  4452. textData.NumExcessFrags = __max(VolData.NumExcessFrags, 0);
  4453. textData.PercentDiskFragged = VolData.PercentDiskFragged;
  4454. if(VolData.TotalFiles){
  4455. textData.AvgFragsPerFile = (VolData.NumExcessFrags + VolData.TotalFiles) * 100 /
  4456. VolData.TotalFiles;
  4457. }
  4458. textData.MFTBytes = VolData.MftSize;
  4459. textData.InUseMFTRecords = VolData.InUseFileRecords;
  4460. textData.TotalMFTRecords = VolData.TotalFileRecords;
  4461. textData.MFTExtents = VolData.MftNumberOfExtents;
  4462. if(VolData.TotalClusters - VolData.UsedClusters){
  4463. if(VolData.NumFreeSpaces){
  4464. textData.FreeSpaceFragPercent = (100 * VolData.NumFreeSpaces) /
  4465. (VolData.TotalClusters - VolData.UsedClusters);
  4466. }
  4467. }
  4468. //If the gui is connected, send gui data to it.
  4469. DataIoClientSetData(ID_REPORT_TEXT_DATA, (TCHAR*)&textData, sizeof(TEXT_DATA), pdataDfrgCtl);
  4470. }
  4471. /*****************************************************************************************************************
  4472. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4473. ROUTINE DESCRIPTION:
  4474. INPUT + OUTPUT:
  4475. RETURN:
  4476. None.
  4477. */
  4478. void SendGraphicsMemoryErr()
  4479. {
  4480. // don't need to send any data
  4481. NOT_DATA NotData;
  4482. _tcscpy(NotData.cVolumeName, VolData.cVolumeName);
  4483. // if the gui is connected, send gui data to it.
  4484. Message(TEXT("engine sending ID_NO_GRAPHICS_MEMORY"), -1, NULL);
  4485. DataIoClientSetData(ID_NO_GRAPHICS_MEMORY, (PTCHAR) &NotData, sizeof(NOT_DATA), pdataDfrgCtl);
  4486. }
  4487. // send error code to client
  4488. // (for command line mode)
  4489. VOID SendErrData(PTCHAR pErrText, DWORD ErrCode)
  4490. {
  4491. static BOOL FirstTime = TRUE;
  4492. // only send the first error
  4493. if (FirstTime)
  4494. {
  4495. // prepare COM message for client
  4496. ERROR_DATA ErrData = {0};
  4497. _tcscpy(ErrData.cVolumeName, VolData.cVolumeName);
  4498. ErrData.dwErrCode = ErrCode;
  4499. if (pErrText != NULL)
  4500. {
  4501. _tcsncpy(ErrData.cErrText, pErrText, 999);
  4502. ErrData.cErrText[999] = TEXT('\0');
  4503. }
  4504. // send COM message to client
  4505. DataIoClientSetData(ID_ERROR, (TCHAR*) &ErrData, sizeof(ERROR_DATA), pdataDfrgCtl);
  4506. // write the error to the error log.
  4507. if (bLogFile && pErrText != NULL)
  4508. {
  4509. WriteErrorToErrorLog(pErrText, -1, NULL);
  4510. }
  4511. // only one error
  4512. FirstTime = FALSE;
  4513. }
  4514. }