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.

314 lines
12 KiB

  1. /**************************************************************************************************
  2. FILENAME: MFTDefrag.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. DESCRIPTION:
  5. MFT defragment for NTFS.
  6. **************************************************************************************************/
  7. #include "stdafx.h"
  8. extern "C"{
  9. #include <string.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. }
  13. #include "Windows.h"
  14. #include <winioctl.h>
  15. #include <math.h>
  16. #include <fcntl.h>
  17. extern "C" {
  18. #include "SysStruc.h"
  19. }
  20. #include "MFTDefrag.h"
  21. #include "DfrgCmn.h"
  22. #include "GetReg.h"
  23. #include "Devio.h"
  24. #include "movefile.h"
  25. #include "Alloc.h"
  26. #include "defragcommon.h"
  27. #define THIS_MODULE 'M'
  28. #include "logfile.h"
  29. #if OPTLONGLONGMATH
  30. #define DIVIDELONGLONGBY32(num) Int64ShraMod32((num), 5)
  31. #define MODULUSLONGLONGBY32(num) ((num) & 0x1F)
  32. #else
  33. #define DIVIDELONGLONGBY32(num) ((num) / 32)
  34. #define MODULUSLONGLONGBY32(num) ((num) % 32)
  35. #endif
  36. /*****************************************************************************************************************
  37. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  38. ROUTINE DESCRIPTION:
  39. The main process of the MFT Defrag functionality
  40. MFT Defrag defragments the MFT file. If the MFT (minus the first extent that includes the
  41. first 16 records) is in two fragments, find a empty space to move it to, and move it, else
  42. dont move it if a space big enough is not found
  43. INPUT:
  44. HANDLE hVolumeHandle Handle to the Volume
  45. LONGLONG BitmapSize The size of the bitmap, since its already known
  46. LONGLONG BytesPerSector The number of Bytes per Sector
  47. LONGLONG TotalClusters Total number of clusters on the drive
  48. ULONGLONG MftZoneStart The Cluster Number of the start of the MFT Zone
  49. ULONGLONG MftZoneEnd The Cluster Number of the end of the MFT Zone
  50. TCHAR tDrive The current drive letter
  51. RETURN:
  52. return TRUE if I was able to defragment the MFT, else I return FALSE if an error occured.
  53. */
  54. BOOL MFTDefrag(
  55. IN HANDLE hVolumeHandle,
  56. IN LONGLONG BitmapSize,
  57. IN LONGLONG BytesPerSector,
  58. IN LONGLONG TotalClusters,
  59. IN ULONGLONG MftZoneStart,
  60. IN ULONGLONG MftZoneEnd,
  61. IN TCHAR tDrive,
  62. IN LONGLONG ClustersPerFRS
  63. )
  64. {
  65. HANDLE hMFTHandle = NULL; //Handle to the MFT file.
  66. LONGLONG lFirstAvailableFreeSpace = 0; //the first available free space on the disk dig enough to move to
  67. ULONGLONG ulMFTsize = 0; //size in clusters of how big the mft file is
  68. LONGLONG lMFTFragments = 0; //the number of fragments in the MFT after excluding the first 16 records
  69. LONGLONG lMFTStartingVcn = 0; //the VCN of the MFT from record 17.
  70. BOOL bReturnValue = TRUE; //value to return
  71. //make sure the drive letter is upper case
  72. tDrive = towupper(tDrive);
  73. Trace(log, "Start: MFTDefrag");
  74. //get the handle to the MFT
  75. TCHAR tMFTPath[100]; //build the path to the MFT
  76. _stprintf(tMFTPath,TEXT("\\\\.\\%c:\\$MFT"),tDrive);
  77. hMFTHandle = CreateFile(tMFTPath,
  78. 0,
  79. FILE_SHARE_READ|FILE_SHARE_WRITE,
  80. NULL,
  81. OPEN_EXISTING,
  82. FILE_FLAG_NO_BUFFERING,
  83. 0);
  84. //check to make sure we got a valid handle (valid handle will be not NULL)
  85. if((hMFTHandle) && (hMFTHandle != INVALID_HANDLE_VALUE))
  86. {
  87. //get the size of the MFT minus the first extent in clusters.
  88. ulMFTsize = GetMFTSize(hMFTHandle, &lMFTFragments, &lMFTStartingVcn);
  89. if(lMFTStartingVcn > (ClustersPerFRS * 16)) //check to see if the first extent is beyond the first 16 record
  90. {
  91. if(lMFTFragments > 1) //mft fragmented go ahead and try and move else just leave
  92. {
  93. //find the first available chunk of free space that is available
  94. lFirstAvailableFreeSpace = FindFreeSpaceChunk(BitmapSize, BytesPerSector,
  95. TotalClusters, ulMFTsize, TRUE, MftZoneStart, MftZoneEnd, hVolumeHandle);
  96. if(lFirstAvailableFreeSpace > 0) //if the first available space is zero, nothing was found dont move
  97. {
  98. //adjust the MFT size down by the size of the first extent, since we do not move
  99. //the first extent
  100. ulMFTsize = ulMFTsize - (ULONGLONG)lMFTStartingVcn;
  101. //move the MFT to new location
  102. if(!MoveFileLocation(hMFTHandle, lFirstAvailableFreeSpace, ulMFTsize, lMFTStartingVcn, hVolumeHandle))
  103. {
  104. //the move failed, so we return FALSE;
  105. Trace(log, "MFTDefrag: MoveFileLocation failed");
  106. bReturnValue = FALSE;
  107. }
  108. }
  109. }
  110. } else
  111. {
  112. //the end of the first extent is not past record 16, so return FALSE;
  113. Trace(log, "MFTDefrag: End of first extent is not past record 16");
  114. bReturnValue = FALSE;
  115. }
  116. CloseHandle(hMFTHandle);
  117. } else
  118. {
  119. Trace(log, "MFTDefrag: Could not open %ws", tMFTPath);
  120. bReturnValue = FALSE; //could not open the MFT
  121. }
  122. Trace(log, "End: MFTDefrag (%s)", bReturnValue ? "succeeded" : "failed");
  123. return bReturnValue;
  124. }
  125. /*****************************************************************************************************************
  126. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  127. ROUTINE DESCRIPTION:
  128. Get the size of the MFT in clusters from calling FSCL_GET_RETRIEVAL_POINTERS.
  129. Also return the number of fragments in the MFT and the starting VCN of the second
  130. fragment of the MFT.
  131. INPUT:
  132. HANDLE hMFTFile The handle to the MFT File
  133. RETURN:
  134. The size of the MFT file in clusters
  135. the number of fragments in the MFT
  136. the starting VCN of the MFT after the first extent
  137. */
  138. ULONGLONG GetMFTSize(
  139. IN HANDLE hMFTHandle,
  140. OUT LONGLONG* lMFTFragments,
  141. OUT LONGLONG* lMFTStartingVcn
  142. )
  143. {
  144. ULONGLONG ulSizeofFileInClusters = 0; //size of the file in clusters
  145. int i;
  146. ULONGLONG startVcn = 0; //starting VCN of the file, always 0
  147. STARTING_VCN_INPUT_BUFFER startingVcn; //starting VCN Buffer
  148. ULONG BytesReturned = 0; //number of bytes returned by ESDeviceIoControl
  149. HANDLE hRetrievalPointersBuffer = NULL; //Handle to the Retrieval Pointers Buffer
  150. PRETRIEVAL_POINTERS_BUFFER pRetrievalPointersBuffer = NULL; //pointer to the Retrieval Pointer
  151. PLARGE_INTEGER pRetrievalPointers = NULL; //Pointer to retrieval pointers
  152. ULONG RetrievalPointers = 0x100; //Number of extents for the file, try 256 first
  153. BOOL bGetRetrievalPointersMore = TRUE; //boolean to test the end of getting retrieval pointers
  154. BOOL bGetRetrievalPointersSuccess = TRUE; //test if I was able to get retrieval pointers
  155. ULONG ulGlobalLockCount = 0; //count the number of times I lock memory
  156. ULONG ii; //index
  157. //initalize the values I give back incase I have an error
  158. *lMFTFragments = 0;
  159. *lMFTStartingVcn = 0;
  160. if (hMFTHandle == INVALID_HANDLE_VALUE)
  161. {
  162. ulSizeofFileInClusters = 0;
  163. } else //continue
  164. {
  165. //zero the memory of the starting VCN input buffer
  166. ZeroMemory(&startVcn, sizeof(STARTING_VCN_INPUT_BUFFER));
  167. //0.1E00 Read the retrieval pointers into a buffer in memory.
  168. while(bGetRetrievalPointersMore){
  169. //0.0E00 Allocate a RetrievalPointersBuffer.
  170. if(!AllocateMemory(sizeof(RETRIEVAL_POINTERS_BUFFER) + (RetrievalPointers * 2 * sizeof(LARGE_INTEGER)),
  171. &hRetrievalPointersBuffer,
  172. (void**)(PCHAR*)&pRetrievalPointersBuffer))
  173. {
  174. ulSizeofFileInClusters = 0;
  175. bGetRetrievalPointersSuccess = FALSE;
  176. break; //break out of the while loop
  177. }
  178. ulGlobalLockCount++;
  179. //call NTControlFile via ESDeviceIoControl to get a buffer with the
  180. //retrieval pointers on the MFT.
  181. startingVcn.StartingVcn.QuadPart = 0;
  182. if(ESDeviceIoControl(hMFTHandle,
  183. FSCTL_GET_RETRIEVAL_POINTERS,
  184. &startingVcn,
  185. sizeof(STARTING_VCN_INPUT_BUFFER),
  186. pRetrievalPointersBuffer,
  187. (DWORD)GlobalSize(hRetrievalPointersBuffer),
  188. &BytesReturned,
  189. NULL))
  190. {
  191. bGetRetrievalPointersMore = FALSE;
  192. bGetRetrievalPointersSuccess = TRUE;
  193. } else
  194. {
  195. //This occurs on a zero length file (no clusters allocated).
  196. if(GetLastError() == ERROR_HANDLE_EOF)
  197. {
  198. //file is zero lenght, so return 0
  199. //free the memory for the retrival pointers
  200. //the while loop makes sure all occurances are unlocked
  201. ulSizeofFileInClusters = 0;
  202. bGetRetrievalPointersSuccess = FALSE;
  203. break; //break out of the while loop
  204. }
  205. //0.0E00 Check to see if the error is not because the buffer is too small.
  206. if(GetLastError() == ERROR_MORE_DATA)
  207. {
  208. //0.1E00 Double the buffer size until it's large enough to hold the file's extent list.
  209. RetrievalPointers *= 2;
  210. } else
  211. {
  212. //some other error, return 0
  213. //free the memory for the retrival pointers
  214. //the while loop makes sure all occurances are unlocked
  215. ulSizeofFileInClusters = 0;
  216. bGetRetrievalPointersSuccess = FALSE;
  217. break; //break out of the while loop
  218. }
  219. }
  220. }
  221. if(bGetRetrievalPointersSuccess) //continue processing if we were able to get retrieval pointers
  222. {
  223. //get the number of fragments in the MFT
  224. *lMFTFragments = pRetrievalPointersBuffer->ExtentCount;
  225. //get the size of the first extend for the starting VCN
  226. *lMFTStartingVcn = pRetrievalPointersBuffer->Extents[0].NextVcn.QuadPart - startVcn;
  227. //loop through the retrival pointer list and add up the size of the file
  228. startVcn = pRetrievalPointersBuffer->StartingVcn.QuadPart;
  229. for (i = 1; i < (ULONGLONG) pRetrievalPointersBuffer->ExtentCount; i++)
  230. {
  231. ulSizeofFileInClusters += pRetrievalPointersBuffer->Extents[i].NextVcn.QuadPart - startVcn;
  232. startVcn = pRetrievalPointersBuffer->Extents[i].NextVcn.QuadPart;
  233. }
  234. }
  235. if(hRetrievalPointersBuffer != NULL)
  236. {
  237. //free the memory for the retrival pointers
  238. //the while loop makes sure all occurances are unlocked
  239. for (ii=0;ii< ulGlobalLockCount;ii++)
  240. {
  241. GlobalUnlock(hRetrievalPointersBuffer);
  242. }
  243. GlobalFree(hRetrievalPointersBuffer);
  244. hRetrievalPointersBuffer = NULL;
  245. }
  246. }
  247. return ulSizeofFileInClusters;
  248. }