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.

955 lines
34 KiB

  1. /*****************************************************************************************************************
  2. FILENAME: MoveFile.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. */
  5. #include "stdafx.h"
  6. #include <windows.h>
  7. #include <winioctl.h>
  8. extern "C" {
  9. #include "SysStruc.h"
  10. }
  11. #include "ErrMacro.h"
  12. #include "DfrgCmn.h"
  13. #include "DfrgEngn.h"
  14. #include "DfrgRes.h"
  15. #include "Devio.h"
  16. #include "Extents.h"
  17. #include "FreeSpace.h"
  18. #include "MoveFile.h"
  19. #include "NtfsSubs.h"
  20. #include "Alloc.h"
  21. #include "DiskView.h"
  22. #include "Event.h"
  23. #include "Logging.h"
  24. #include "FsSubs.h"
  25. #define THIS_MODULE 'O'
  26. #include "logfile.h"
  27. static void UpdatePostMoveStats(FILE_EXTENT_HEADER* pFileExtentHeader, LONGLONG llBeforeExtents);
  28. /*****************************************************************************************************************
  29. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  30. ROUTINE DESCRIPTION:
  31. Moves a file to a new location on a disk (so as to defrag it).
  32. GLOBALS:
  33. VolData
  34. RETURN:
  35. TRUE = Success
  36. FALSE = Failure
  37. */
  38. BOOL
  39. MoveFile(
  40. )
  41. {
  42. TCHAR cString[500];
  43. UINT i, j;
  44. FILE_EXTENT_HEADER* pFileExtentHeader;
  45. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  46. EXTENT_LIST* pExtents;
  47. LONGLONG VirtualFileClusters;
  48. LONGLONG llBeforeExtents;
  49. ATTRIBUTE_TYPE_CODE TypeCode = 0;
  50. LONGLONG Vcn = 0;
  51. LONGLONG Lcn = VolData.FoundLcn;
  52. LONGLONG RunLength = VolData.FoundLen;
  53. LONGLONG oldStartingLcn = 0;
  54. BOOL bReturnValue = FALSE; // assume error
  55. BOOL bFileStartedFragmented; // keeps track of the starting state of a file
  56. // Validate that we have a file system.
  57. if((VolData.FileSystem != FS_NTFS)
  58. && (VolData.FileSystem != FS_FAT)
  59. && (VolData.FileSystem != FS_FAT32)) {
  60. EF_ASSERT(FALSE);
  61. }
  62. // Debug
  63. // ShowExtentList();
  64. // Set the status to move the next file. Therefore, unless this variable gets reset
  65. // to NEXT_ALGO_STEP within this function, it goes on to the next file by default.
  66. #ifdef DFRGNTFS
  67. VolData.Status = ERROR_SUCCESS;
  68. #elif DFRGFAT
  69. VolData.Status = NEXT_FILE;
  70. #endif
  71. // Set up the Extent pointers structure to fill in the extent list in VolData.
  72. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  73. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  74. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  75. //Scott Sipe 5/1/2000 Boot Optimize
  76. //we now have the extents for the file, it the file is part of the files that have been
  77. //moved by the BootOptimize routine, then we do not want to move them, so just test to see
  78. //if the pExtents->StartingLcn is in the block of optimized files, then exit the routine and
  79. //return FALSE
  80. #ifdef DFRGFAT
  81. if((VolData.BootOptimizeBeginClusterExclude != 0 ||
  82. VolData.BootOptimizeEndClusterExclude != 0) && IsBootVolume(VolData.cDrive))
  83. {
  84. if(pExtents->StartingLcn >= (LONGLONG)VolData.BootOptimizeBeginClusterExclude &&
  85. pExtents->StartingLcn < (LONGLONG)VolData.BootOptimizeEndClusterExclude)
  86. {
  87. return(FALSE);
  88. }
  89. }
  90. #endif
  91. __try {
  92. // Mark the file as free space before we move it. After we move it,
  93. // the new blocks will be colored in with the correct color.
  94. if (!AddExtents(FreeSpaceColor)){
  95. EH(FALSE);
  96. __leave;
  97. }
  98. // Record the number of extents in the stream before we move it.
  99. llBeforeExtents = pFileExtentHeader->ExcessExtents;
  100. // Loop through each stream in the file and move them.
  101. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++) {
  102. // If this is not the first stream, then get a handle to the current stream.
  103. // We already have a handle to the first stream when this function is called.
  104. if(i) {
  105. #ifdef DFRGNTFS
  106. TCHAR StreamName[MAX_PATH];
  107. // Get this stream's streamname.
  108. if (!GetStreamNameAndTypeFromNumber(i, StreamName, &TypeCode, NULL)){
  109. EH(FALSE);
  110. __leave;
  111. }
  112. // save a copy of the file name so that we can reset it later
  113. PTCHAR strFileName = new TCHAR[VolData.vFileName.GetLength()+1];
  114. if ((strFileName == NULL) || VolData.vFileName.IsEmpty()) {
  115. if (strFileName) {
  116. delete [] strFileName;
  117. strFileName = NULL;
  118. }
  119. EH(FALSE);
  120. __leave;
  121. }
  122. _tcscpy(strFileName, VolData.vFileName.GetBuffer());
  123. // Append this stream's filename to the end of the file's name.
  124. // Note, if there was already another stream's name appended, then
  125. // this will simply overwrite it directly. This puts the colon
  126. // between the filename and the streamname.
  127. VolData.vFileName.AddChar(L':');
  128. // This copies streamname plus terminator to the end.
  129. VolData.vFileName += StreamName;
  130. // Opens the stream.
  131. if (!OpenNtfsFile()){
  132. delete [] strFileName;
  133. strFileName = NULL;
  134. EH(FALSE);
  135. __leave;
  136. }
  137. // reset it (remove the stream name) so that the next stream will work ok
  138. VolData.vFileName = strFileName;
  139. delete [] strFileName;
  140. strFileName = NULL;
  141. #else
  142. // If this is not NTFS, then there can't be multiple streams!
  143. EH_ASSERT(FALSE);
  144. __leave;
  145. #endif
  146. }
  147. // Calculate how many clusters there are virtually in this stream
  148. // (more than there are on the disk if this is a compressed file).
  149. if (VolData.BytesPerCluster == 0){
  150. EH_ASSERT(FALSE);
  151. __leave;
  152. }
  153. VirtualFileClusters = pStreamExtentHeader->AllocatedLength / VolData.BytesPerCluster;
  154. if (pStreamExtentHeader->AllocatedLength % VolData.BytesPerCluster) {
  155. VirtualFileClusters++;
  156. }
  157. // Say where we're moving the file to.
  158. wsprintf(cString,
  159. TEXT("Moving file %#lx at Vcn %#lx to Lcn %#lx for %#lx"),
  160. (ULONG)VolData.FileRecordNumber,
  161. (ULONG)Vcn,
  162. (ULONG)Lcn,
  163. (ULONG)VirtualFileClusters);
  164. Message(cString, -1, NULL);
  165. // stats for move attempts
  166. if (VolData.bFragmented){
  167. VolData.FragmentedFileMovesAttempted[VolData.Pass]++;
  168. bFileStartedFragmented = TRUE;
  169. }
  170. else{
  171. VolData.ContiguousFileMovesAttempted[VolData.Pass]++;
  172. bFileStartedFragmented = FALSE;
  173. }
  174. // Try to move the file - Don't use the physical cluster count, use the virtual.
  175. if (!MoveAPieceOfAFile(Vcn, Lcn, VirtualFileClusters)){
  176. __leave;
  177. }
  178. // capture the old starting lcn so that we can
  179. // detect if it moved...
  180. // 'cause sometimes it moves, but not to where
  181. // we tell it to. Bummer.
  182. oldStartingLcn = pExtents->StartingLcn;
  183. // Get the file's new extent list
  184. if (!GetExtentList(DEFAULT_STREAMS, NULL)){
  185. EH(FALSE);
  186. __leave;
  187. }
  188. // Reset the file, stream and extent pointers because VolData.pExtentList might
  189. // have been realloced in GetExtentList()
  190. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  191. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList
  192. + sizeof(FILE_EXTENT_HEADER));
  193. // Find the stream we were working on before.
  194. // Loop through until we hit the one we were on, each time
  195. // bumping the stream header to the next stream.
  196. for(j=0; j<i; j++) {
  197. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader
  198. + sizeof(STREAM_EXTENT_HEADER)
  199. + pStreamExtentHeader->ExtentCount
  200. * sizeof(EXTENT_LIST));
  201. }
  202. // Make sure we didn't go off into never-never-land.
  203. if (j >= pFileExtentHeader->NumberOfStreams){
  204. EH(FALSE);
  205. __leave;
  206. }
  207. // We have to get a pointer to the extents again.
  208. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  209. // Confirm that the begining of the stream moved.
  210. // If not the move failed, try the next algorithm.
  211. if(pExtents->StartingLcn == oldStartingLcn) {
  212. Message(TEXT("ERROR - MoveFile - Stream didn't move. Go to next file."), -1, NULL);
  213. //Since we had a problem, flush the volume.
  214. Message(TEXT("MoveFile - Flushing Volume"), -1, NULL);
  215. EH(FlushFileBuffers(VolData.hVolume));
  216. VolData.VolumeBufferFlushes[VolData.Pass]++;
  217. #ifdef DFRGFAT
  218. VolData.Status = NEXT_FILE;
  219. #endif
  220. __leave;
  221. }
  222. Lcn += VirtualFileClusters;
  223. // Move the stream header to the next stream.
  224. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader
  225. + sizeof(STREAM_EXTENT_HEADER)
  226. + pStreamExtentHeader->ExtentCount
  227. * sizeof(EXTENT_LIST));
  228. }
  229. UpdatePostMoveStats(pFileExtentHeader, llBeforeExtents);
  230. bReturnValue = TRUE; // everything went just peachy
  231. }
  232. __finally {
  233. // Validate data and keep track of the percent of the disk that is fragmented.
  234. if (VolData.UsedSpace != 0) {
  235. VolData.PercentDiskFragged = 100 * VolData.FraggedSpace / VolData.UsedSpace;
  236. }
  237. else if (VolData.UsedClusters != 0 && VolData.BytesPerCluster != 0) {
  238. VolData.PercentDiskFragged = (100 * VolData.FraggedSpace) /
  239. (VolData.UsedClusters * VolData.BytesPerCluster);
  240. }
  241. // Mark the file's new clusters on the disk in the diskview with the appropriate color.
  242. if(VolData.bDirectory) {
  243. AddExtents(DirectoryColor);
  244. }
  245. else {
  246. if(VolData.bFragmented){
  247. AddExtents(FragmentColor);
  248. }
  249. else{
  250. AddExtents(UsedSpaceColor);
  251. }
  252. }
  253. #ifdef DFRGFAT
  254. // Update the file in its file list.
  255. if (!UpdateInFileList()){
  256. EH(FALSE);
  257. }
  258. else {
  259. // Load the volume bitmap. Go to the next pass if there is an error.
  260. if(!GetVolumeBitmap()) {
  261. Message(TEXT("ERROR - MoveFile - GetVolumeBitmap."), -1, NULL);
  262. VolData.Status = NEXT_PASS;
  263. bReturnValue = FALSE;
  264. }
  265. }
  266. }
  267. if (bReturnValue) { // move succeeded
  268. VolData.FilesMoved++;
  269. VolData.FilesMovedInLastPass++;
  270. // stats for move attempts that succeeded
  271. if (bFileStartedFragmented){
  272. VolData.FragmentedFileMovesSucceeded[VolData.Pass]++;
  273. }
  274. else{
  275. VolData.ContiguousFileMovesSucceeded[VolData.Pass]++;
  276. }
  277. }
  278. else {
  279. // stats for move attempts that failed
  280. if (bFileStartedFragmented){
  281. VolData.FragmentedFileMovesFailed[VolData.Pass]++;
  282. }
  283. else{
  284. VolData.ContiguousFileMovesFailed[VolData.Pass]++;
  285. }
  286. #endif
  287. }
  288. // Debug
  289. // ShowExtentList();
  290. return bReturnValue;
  291. }
  292. /*****************************************************************************************************************
  293. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  294. ROUTINE DESCRIPTION:
  295. This routine will partially defragment a file.
  296. GLOBALS:
  297. VolData
  298. RETURN:
  299. TRUE = Success
  300. FALSE = Failure
  301. */
  302. BOOL
  303. PartialDefrag(
  304. )
  305. {
  306. TCHAR cString[500];
  307. UINT i;
  308. FILE_EXTENT_HEADER* pFileExtentHeader;
  309. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  310. EXTENT_LIST* pExtents;
  311. LONGLONG llBeforeExtents;
  312. LONGLONG VirtualFileClusters;
  313. ATTRIBUTE_TYPE_CODE TypeCode = 0;
  314. LONGLONG llCurrentFreeCount = 0xffffffffffffffff;
  315. LONGLONG llCurrentFreeLcn = 0xffffffffffffffff;
  316. LONGLONG llStreamClusterTotal = 0xffffffffffffffff;
  317. LONGLONG llRemainingStreamClusters = 0xffffffffffffffff;
  318. LONGLONG llCurrentChunkClusters = 0xffffffffffffffff;
  319. LONGLONG Vcn = 0;
  320. LONGLONG ExcessStreamExtents = 0;
  321. LONGLONG StreamClusters = 0;
  322. EXTENT_LIST* pFreeExtents = NULL;
  323. ULONG ulFreeExtent = 0;
  324. BOOL bReturnValue = FALSE; // assume error
  325. #ifdef DFRGFAT
  326. // Set the status to move the next file. Therefore, unless this variable gets reset
  327. // to NEXT_ALGO_STEP within this function, it goes on to the next file by default.
  328. VolData.Status = NEXT_FILE;
  329. // Validate that we have a file system.
  330. if((VolData.FileSystem != FS_NTFS)
  331. && (VolData.FileSystem != FS_FAT)
  332. && (VolData.FileSystem != FS_FAT32)) {
  333. EF_ASSERT(FALSE);
  334. }
  335. // Debug
  336. //ShowExtentList();
  337. #endif
  338. //Set up the Extent pointers structure to fill in the extent list in VolData.
  339. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  340. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  341. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  342. __try {
  343. // Get a pointer to the free space extents
  344. pFreeExtents = (EXTENT_LIST*)GlobalLock((void*)VolData.hFreeExtents);
  345. if (pFreeExtents == (EXTENT_LIST*) NULL) {
  346. EH(FALSE);
  347. __leave;
  348. }
  349. // check to see if the move gets you anywhere!
  350. // find out how many free space extents it takes to hold this file
  351. LONGLONG llClusterCount = 0;
  352. UINT uFreeSpaceExtentCount = 0;
  353. for(i=0; i < VolData.FreeExtents; i++) {
  354. llClusterCount += pFreeExtents[i].ClusterCount;
  355. uFreeSpaceExtentCount++;
  356. if (llClusterCount >= VolData.NumberOfClusters){
  357. break;
  358. }
  359. }
  360. // if it would fragment the file more or if it won't even fit in the existing free space, bag it!
  361. if (uFreeSpaceExtentCount >= VolData.NumberOfFragments || llClusterCount < VolData.NumberOfClusters){
  362. Message(TEXT("This PartialDefrag() would fragment the file more - return"), -1, NULL);
  363. __leave;
  364. }
  365. _stprintf(cString,
  366. TEXT("Partial Defrag is defragmenting from %I64d fragments down to %d fragments"),
  367. VolData.NumberOfFragments, uFreeSpaceExtentCount);
  368. Message(cString, -1, NULL);
  369. // Mark the file as free space before we move it. After we move it,
  370. // the new blocks will be colored in with the correct color.
  371. if (!AddExtents(FreeSpaceColor)){
  372. EH(FALSE);
  373. __leave;
  374. }
  375. // Record the number of extents in the stream before we move it.
  376. llBeforeExtents = pFileExtentHeader->ExcessExtents;
  377. #ifdef ESI_MESSAGE_WINDOW
  378. #ifdef DFRGNTFS
  379. /* DEBUG stuff...
  380. for(i=0; i < VolData.FreeExtents; i++) {
  381. // Say where we're moving the file to.
  382. wsprintf(cString,
  383. TEXT("#%i freespace extnt Lcn %#lx:%#lx"),
  384. (UINT)i,
  385. (ULONG) pFreeExtents[i].StartingLcn,
  386. (ULONG) pFreeExtents[i].ClusterCount);
  387. Message(cString, -1, NULL);
  388. }
  389. */
  390. #endif
  391. #endif
  392. //Make sure we start using up free space at the beginning of the
  393. //freespace extent list
  394. ulFreeExtent = 0;
  395. // Loop through each stream in the file and move them.
  396. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++) {
  397. // If this is not the first stream, then get a handle to the current stream.
  398. // We already have a handle to the first stream when this function is called.
  399. if(i>0) {
  400. #ifdef DFRGNTFS
  401. TCHAR StreamName[MAX_PATH];
  402. //Get this stream's streamname.
  403. if (!GetStreamNameAndTypeFromNumber(i, StreamName, &TypeCode, NULL)){
  404. EH(FALSE);
  405. __leave;
  406. }
  407. // save a copy of the file name so that we can reset it later
  408. PTCHAR strFileName = new TCHAR[VolData.vFileName.GetLength()+1];
  409. if ((strFileName == NULL) || VolData.vFileName.IsEmpty()){
  410. if (strFileName) {
  411. delete [] strFileName;
  412. strFileName = NULL;
  413. }
  414. EH(FALSE);
  415. __leave;
  416. }
  417. _tcscpy(strFileName, VolData.vFileName.GetBuffer());
  418. // Append this stream's filename to the end of the file's name.
  419. // Note, if there was already another stream's name appended, then
  420. // this will simply overwrite it directly. This puts the colon
  421. // between the filename and the streamname.
  422. VolData.vFileName.AddChar(L':');
  423. // This copies streamname plus terminator to the end.
  424. VolData.vFileName += StreamName;
  425. // Opens the stream.
  426. if (!OpenNtfsFile()){
  427. delete [] strFileName;
  428. strFileName = NULL;
  429. EH(FALSE);
  430. __leave;
  431. }
  432. // reset it (remove the stream name) so that the next stream will work ok
  433. VolData.vFileName = strFileName;
  434. delete [] strFileName;
  435. strFileName = NULL;
  436. #else
  437. //If this is not NTFS, then there can't be multiple streams!
  438. EH_ASSERT(FALSE);
  439. __leave;
  440. #endif
  441. }
  442. // Get the set of free space clusters to use next
  443. llCurrentFreeCount = pFreeExtents[ulFreeExtent].ClusterCount;
  444. llCurrentFreeLcn = pFreeExtents[ulFreeExtent].StartingLcn;
  445. Vcn = 0;
  446. VirtualFileClusters = 0;
  447. // Make sure we know how many clusters comprise this stream
  448. llStreamClusterTotal = pStreamExtentHeader->AllocatedLength / VolData.BytesPerCluster;
  449. if ((pStreamExtentHeader->AllocatedLength % VolData.BytesPerCluster) != 0) {
  450. llStreamClusterTotal++;
  451. }
  452. //setup a variable we can count down until the stream is exhausted
  453. llRemainingStreamClusters = llStreamClusterTotal;
  454. //this loop gets performed until the stream is exhausted
  455. while (llRemainingStreamClusters > 0) {
  456. // Sleep if paused.
  457. while(VolData.EngineState == PAUSED){
  458. Sleep(1000);
  459. }
  460. // Terminate if told to stop by the controller - this is not an error.
  461. if (VolData.EngineState == TERMINATE){
  462. __leave;
  463. }
  464. //if the remaining clusters are greater than or equal to the current
  465. //free space chunk, use up the whole chunk
  466. if (llRemainingStreamClusters >= llCurrentFreeCount) {
  467. llCurrentChunkClusters = llCurrentFreeCount;
  468. }
  469. //if the remaining clusters is less than the current
  470. //free space chunk, figure out how much of the chunk to use
  471. else {
  472. llCurrentChunkClusters = llRemainingStreamClusters;
  473. llCurrentChunkClusters = (llCurrentChunkClusters +15)&0xfffffffffffffff0;
  474. }
  475. //debug only
  476. // Say where we're moving the file to.
  477. wsprintf(cString,
  478. TEXT("Moving file %#lx at Vcn %#lx to Lcn %#lx for %#lx"),
  479. (ULONG)VolData.FileRecordNumber,
  480. (ULONG)Vcn,
  481. (ULONG)llCurrentFreeLcn,
  482. (ULONG)llCurrentChunkClusters);
  483. Message(cString, -1, NULL);
  484. // Try to move the file - Don't use the physical cluster count, use the virtual.
  485. if (!MoveAPieceOfAFile(Vcn, llCurrentFreeLcn, llCurrentChunkClusters)){
  486. __leave;
  487. }
  488. //********************************************************************
  489. // Note: the previous incarnation of this code went to great lengths to
  490. // determine if a partial move was done. By experiment this is totally
  491. // unnecessary. The "partial move" was always picked up in the call to
  492. // moveapieceofafile and we left, skipping to the next file anyway. So
  493. // I didn't bother accounting for the partial move case in my rewrite.
  494. // jlj 15apr99
  495. //********************************************************************
  496. // Update the Vcn to point after the clusters just moved.
  497. Vcn = Vcn + llCurrentChunkClusters;
  498. // Get the set of free space clusters to use next
  499. // (it's possible we didn't use all of them, so just decrement
  500. // until it's exhausted)
  501. llCurrentFreeCount = llCurrentFreeCount - llCurrentChunkClusters;
  502. // Okay, was it exhausted?
  503. if (llCurrentFreeCount <= 0) {
  504. // means we ran out of free spac in current chunk; get next one
  505. ulFreeExtent++;
  506. //if we try to get more than we originally found (as stored in
  507. // VolData.FreeExtents) then bail out.
  508. if (ulFreeExtent > VolData.FreeExtents) {
  509. Message(TEXT("ERROR: PartialDefrag Ran out of Free Exents"), -1, NULL);
  510. EH_ASSERT(FALSE);
  511. __leave;
  512. }
  513. // Get the set of free space clusters to use next
  514. llCurrentFreeCount = pFreeExtents[ulFreeExtent].ClusterCount;
  515. llCurrentFreeLcn = pFreeExtents[ulFreeExtent].StartingLcn;
  516. }
  517. else {
  518. //if space left in current free extent, then
  519. //just update the current pointer and continue
  520. llCurrentFreeLcn = llCurrentFreeLcn + llCurrentChunkClusters;
  521. }
  522. //okay, it's all done, just countdown the remaining clusters and continue
  523. llRemainingStreamClusters =
  524. llRemainingStreamClusters - llCurrentChunkClusters;
  525. }
  526. // come here when done with the current stream
  527. //get the current extent list
  528. EF(GetExtentList(DEFAULT_STREAMS, NULL));
  529. //Set up the Extent pointers structure to fill in the extent list in VolData.
  530. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  531. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  532. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  533. // Move the stream header to the next stream.
  534. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader
  535. + sizeof(STREAM_EXTENT_HEADER)
  536. + pStreamExtentHeader->ExtentCount
  537. * sizeof(EXTENT_LIST));
  538. }
  539. // come here when all the streams are exhausted (i.e. all done with file)
  540. //when we're all done with the file, do the stats
  541. UpdatePostMoveStats(pFileExtentHeader, llBeforeExtents);
  542. //and consider we finished with no errors
  543. bReturnValue = TRUE;
  544. }
  545. __finally {
  546. //come here when all done with the file processing or if any
  547. //error occurred. Any "clean up the partial move" is accounted for
  548. //here.
  549. //get the current extent list
  550. if (GetExtentList(DEFAULT_STREAMS, NULL)){
  551. // Validate data and keep track of the percent of the disk that is fragmented.
  552. if (VolData.UsedSpace != 0) {
  553. VolData.PercentDiskFragged = 100 * VolData.FraggedSpace / VolData.UsedSpace;
  554. }
  555. else if (VolData.UsedClusters != 0 && VolData.BytesPerCluster != 0) {
  556. VolData.PercentDiskFragged = (100 * VolData.FraggedSpace) /
  557. (VolData.UsedClusters * VolData.BytesPerCluster);
  558. }
  559. // Mark the file's new clusters on the disk in the diskview with the appropriate color.
  560. if(VolData.bDirectory) {
  561. AddExtents(DirectoryColor);
  562. }
  563. else {
  564. if(VolData.bFragmented) {
  565. AddExtents(FragmentColor);
  566. }
  567. else {
  568. AddExtents(UsedSpaceColor);
  569. }
  570. }
  571. //Update the file's spot in the file list.
  572. if (!UpdateInFileList()){
  573. bReturnValue = FALSE;
  574. EH(FALSE);
  575. }
  576. else {
  577. //0.0E00 Load the volume bitmap. Go to the next pass if there is an error.
  578. if(!GetVolumeBitmap()) {
  579. Message(TEXT("ERROR - PartialDefrag - GetVolumeBitmap()"), -1, NULL);
  580. #ifdef DFRGFAT
  581. VolData.Status = NEXT_PASS;
  582. #endif
  583. bReturnValue = FALSE;
  584. }
  585. }
  586. }
  587. // do the stats for this if we figure we moved it in its entirety
  588. if (bReturnValue) {
  589. VolData.FilesMoved++;
  590. VolData.FilesMovedInLastPass++;
  591. }
  592. }
  593. return bReturnValue;
  594. }
  595. /*****************************************************************************************************************
  596. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  597. ROUTINE DESCRIPTION:
  598. This routine moves a section (or all) of a file to a new location on the disk.
  599. GLOBALS:
  600. VolData
  601. RETURN:
  602. TRUE = Success
  603. FALSE = Failure
  604. */
  605. BOOL
  606. MoveAPieceOfAFile(
  607. IN LONGLONG FileVcn,
  608. IN LONGLONG FreeLcn,
  609. IN LONGLONG VirtualClustersToMove
  610. )
  611. {
  612. MOVE_FILE_DATA MoveData;
  613. ULONG BytesReturned;
  614. LONGLONG ntStatus;
  615. DWORD dwError = ERROR_SUCCESS;
  616. // Three tries
  617. for (int i=0; i<3; i++) {
  618. // Initialize the call to the hook to move this file.
  619. MoveData.FileHandle = VolData.hFile;
  620. MoveData.StartingVcn.QuadPart = FileVcn;
  621. MoveData.StartingLcn.QuadPart = FreeLcn;
  622. MoveData.ClusterCount = (ULONG)VirtualClustersToMove;
  623. // Call the MoveFile hook.
  624. if(ESDeviceIoControl(VolData.hVolume,
  625. FSCTL_MOVE_FILE,
  626. &MoveData,
  627. sizeof(MOVE_FILE_DATA),
  628. &ntStatus,
  629. sizeof(LONGLONG),
  630. &BytesReturned,
  631. NULL)) {
  632. #ifdef DFRGNTFS
  633. VolData.Status = ERROR_SUCCESS;
  634. #elif DFRGFAT
  635. VolData.Status = NEXT_FILE;
  636. #endif
  637. return TRUE;
  638. }
  639. // Get the error and display.
  640. dwError = GetLastError();
  641. #ifdef DFRGNTFS
  642. VolData.Status = dwError;
  643. #endif
  644. Message(TEXT("MoveAPieceOfAFile - GetLastError = "), dwError, NULL);
  645. Message(TEXT("MoveAPieceOfAFile - ntStatus = "), (DWORD)ntStatus, NULL);
  646. Trace(log, " FSCTL_MOVE_FILE failed. "
  647. "File FRN:%I64d StartingLcn:%I64d ClusterCount:%I64d. "
  648. "Free-space StartingLcn:%I64d ClusterCount:%I64d. Error %lu",
  649. VolData.FileRecordNumber, VolData.StartingLcn, VolData.NumberOfClusters,
  650. VolData.FoundLcn, VolData.FoundLen, dwError);
  651. // This is what is returned if the hook does not have directory move enabled.
  652. if (dwError == ERROR_INVALID_PARAMETER) {
  653. // Go to the next file.
  654. #ifdef DFRGFAT
  655. VolData.Status = NEXT_FILE;
  656. #endif
  657. return FALSE;
  658. }
  659. // We got an error-retry sleep and try again.
  660. else if (dwError == ERROR_RETRY) {
  661. Sleep(300);
  662. }
  663. // Some other error, break from the loop and handle below.
  664. else {
  665. break;
  666. }
  667. }
  668. // In all other error cases flush the volume.
  669. Message(TEXT("Flushing Volume"), -1, NULL);
  670. VolData.VolumeBufferFlushes[VolData.Pass]++;
  671. EH(FlushFileBuffers(VolData.hVolume));
  672. #ifdef DFRGNTFS
  673. VolData.Status = dwError;
  674. #elif DFRGFAT
  675. // If an error occured go to the next algorithm step.
  676. VolData.Status = NEXT_ALGO_STEP;
  677. #endif
  678. return FALSE;
  679. }
  680. /*****************************************************************************************************************
  681. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  682. ROUTINE DESCRIPTION:
  683. This routine can be used during debugging to display the extent list for a file.
  684. GLOBALS:
  685. IN VolData.pExtentList - The extent list for the file to show.
  686. IN VolData.NumberOfFragments - The number of extents in the extent list.
  687. RETURN:
  688. TRUE = Success
  689. FALSE = Failure
  690. */
  691. VOID
  692. ShowExtentList(
  693. )
  694. {
  695. EXTENT_LIST* pExtentList;
  696. LONGLONG Extent;
  697. TCHAR cString[300];
  698. pExtentList = (EXTENT_LIST*)(VolData.pExtentList
  699. + sizeof(STREAM_EXTENT_HEADER)
  700. + sizeof(FILE_EXTENT_HEADER)
  701. );
  702. for(Extent = 0;
  703. Extent < VolData.NumberOfFragments;
  704. Extent ++) {
  705. wsprintf(cString,
  706. TEXT("StartingLcn = 0x%lx ClusterCount = 0x%lx"),
  707. (ULONG)((pExtentList[Extent].StartingLcn != 0xFFFFFFFFFFFFFFFF) ?
  708. pExtentList[Extent].StartingLcn : 0),
  709. (ULONG)pExtentList[Extent].ClusterCount);
  710. Message(cString, -1, NULL);
  711. }
  712. }
  713. /**/
  714. static void UpdatePostMoveStats(FILE_EXTENT_HEADER* pFileExtentHeader, LONGLONG llBeforeExtents)
  715. {
  716. if (VolData.bDirectory) {
  717. // Update the number of excees directory fragments.
  718. VolData.NumExcessDirFrags -= llBeforeExtents - pFileExtentHeader->ExcessExtents;
  719. // Directory was defragmented.
  720. if(llBeforeExtents && !pFileExtentHeader->ExcessExtents) {
  721. VolData.NumFraggedDirs--;
  722. VolData.FraggedSpace -= VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  723. Message(TEXT("The directory has been successfully defragmented."), -1, NULL);
  724. EH(LogEvent(MSG_ENGINE_DEFRAGMENT, ESICompressFilePath(VolData.cFileName)));
  725. }
  726. // Move of contiguous directory to consolidate free space.
  727. // This includes compressed directories with contiguous extents.
  728. else if(llBeforeExtents == pFileExtentHeader->ExcessExtents) {
  729. Message(TEXT("The directory has been successfully moved to consolidate free space."), -1, NULL);
  730. EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName)));
  731. }
  732. // The directory was contiguous and was fragmented to consolidate free space.
  733. else if(!llBeforeExtents && pFileExtentHeader->ExcessExtents) {
  734. VolData.NumFraggedDirs++;
  735. VolData.FraggedSpace += VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  736. Message(TEXT("The directory has been temporarily fragmented in order to consolidate free space."), -1, NULL);
  737. EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName)));
  738. }
  739. }
  740. // Update the disk fragmentation and file fragmentation stats.
  741. else {
  742. // Update the number of excees file fragments.
  743. VolData.NumExcessFrags -= llBeforeExtents - pFileExtentHeader->ExcessExtents;
  744. // File was defragmented file.
  745. if(llBeforeExtents && !pFileExtentHeader->ExcessExtents) {
  746. VolData.NumFraggedFiles--;
  747. VolData.FraggedSpace -= VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  748. Message(TEXT("The file has been successfully defragmented."), -1, NULL);
  749. EH(LogEvent(MSG_ENGINE_DEFRAGMENT, ESICompressFilePath(VolData.cFileName)));
  750. }
  751. // Move of contiguous file to consolidate free space.
  752. // This includes compressed files with contiguous extents.
  753. else if(llBeforeExtents == pFileExtentHeader->ExcessExtents) {
  754. Message(TEXT("The file has been successfully moved to consolidate free space."), -1, NULL);
  755. EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName)));
  756. }
  757. // The directory was contiguous and was fragmented to consolidate free space.
  758. else if(!llBeforeExtents && pFileExtentHeader->ExcessExtents) {
  759. VolData.NumFraggedFiles++;
  760. VolData.FraggedSpace += VolData.NumberOfRealClusters * VolData.BytesPerCluster;
  761. Message(TEXT("The file has been temporarily fragmented in order to consolidate free space."), -1, NULL);
  762. EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName)));
  763. }
  764. // Validate data - keep track of the average number of fragments per file.
  765. if((VolData.NumFraggedFiles != 0) && (VolData.CurrentFile != 0)) {
  766. VolData.AveFragsPerFile =
  767. (pFileExtentHeader->ExcessExtents + VolData.CurrentFile) * 100 /
  768. VolData.CurrentFile;
  769. }
  770. else {
  771. VolData.AveFragsPerFile = 100;
  772. }
  773. }
  774. }