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.

468 lines
14 KiB

  1. /**************************************************************************************************
  2. FILENAME: defragcommon.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. DESCRIPTION:
  5. Common routines used in MFTdefrag and bootoptimize.
  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. #include <vss.h>
  18. #include <vswriter.h>
  19. #include <vsbackup.h> // IsVolumeSnapshotted
  20. extern "C" {
  21. #include "SysStruc.h"
  22. }
  23. #include "defragcommon.h"
  24. #include "DfrgCmn.h"
  25. #include "GetReg.h"
  26. #include "Devio.h"
  27. #include "FreeSpace.h"
  28. #include "Alloc.h"
  29. //#include "Message.h"
  30. extern HWND hwndMain;
  31. extern BOOL bCommandLineMode;
  32. #if OPTLONGLONGMATH
  33. #define DIVIDELONGLONGBY32(num) Int64ShraMod32((num), 5)
  34. #define MODULUSLONGLONGBY32(num) ((num) & 0x1F)
  35. #else
  36. #define DIVIDELONGLONGBY32(num) ((num) / 32)
  37. #define MODULUSLONGLONGBY32(num) ((num) % 32)
  38. #endif
  39. /*****************************************************************************************************************
  40. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  41. ROUTINE DESCRIPTION:
  42. Returns the location of the first free space chunk found of size Get the size of the file in clusters from calling FSCL_GET_RETRIEVAL_POINTERS.
  43. INPUT:
  44. LONGLONG BitmapSize The size of the bitmap for the volume
  45. LONGLONG BytesPerSector The bytes per sector
  46. LONGLONG TotalClusters The total clusters on the drive
  47. ULONGLONG lMFTsize The size of the MFT to look for
  48. ULONGLONG MftZoneStart The start of the MFT Zone
  49. ULONGLONG MftZoneEnd The end of the MFT Zone
  50. HANDLE hVolumeHandle Volume HANDLE
  51. RETURN:
  52. The starting cluster of where free space is located, or 0 if not found
  53. */
  54. ULONGLONG FindFreeSpaceChunk(
  55. IN LONGLONG BitmapSize,
  56. IN LONGLONG BytesPerSector,
  57. IN LONGLONG TotalClusters,
  58. IN ULONGLONG ulFileSize,
  59. IN BOOL IsNtfs,
  60. IN ULONGLONG MftZoneStart,
  61. IN ULONGLONG MftZoneEnd,
  62. IN HANDLE hVolumeHandle
  63. )
  64. {
  65. STARTING_LCN_INPUT_BUFFER StartingLcnInputBuffer; //input buffer for FSCTL_GET_VOLUME_BITMAP
  66. PVOLUME_BITMAP_BUFFER pVolumeBitmap = NULL; //pointer to Volume Bitmap
  67. HANDLE hVolumeBitmap = NULL; //Handle to Volume Bitmap
  68. PULONG pBitmap = NULL; //pointer to the volume Bitmap
  69. ULONG BytesReturned = 0; //number of bytes returned from ESDeviceIoControl
  70. BOOL bRetStatus = FALSE; // assume an error
  71. LONGLONG SearchStartLcn = 0; //The LCN of where to start searching for free space
  72. LONGLONG FreeStartLcn = 0; //The LCN of where free space starts
  73. LONGLONG FreeCount = 0; //The size of the free space in clusters
  74. //set the starting LCN to 0
  75. StartingLcnInputBuffer.StartingLcn.QuadPart = 0;
  76. if(!AllocateMemory((DWORD)(sizeof(VOLUME_BITMAP_BUFFER) + (BitmapSize / 8) + 1 + BytesPerSector),
  77. &hVolumeBitmap,
  78. NULL))
  79. {
  80. FreeStartLcn = 0;
  81. } else //continue processing
  82. {
  83. //0.0E00 Lock and clear the bitmap buffer
  84. pVolumeBitmap = (PVOLUME_BITMAP_BUFFER) GlobalLock(hVolumeBitmap);
  85. if (pVolumeBitmap == (PVOLUME_BITMAP_BUFFER) NULL)
  86. {
  87. FreeStartLcn = 0;
  88. } else //continue processing
  89. {
  90. ZeroMemory(pVolumeBitmap, (DWORD)(sizeof(VOLUME_BITMAP_BUFFER) + (BitmapSize / 8)));
  91. //0.0E00 Load the bitmap
  92. StartingLcnInputBuffer.StartingLcn.QuadPart = 0;
  93. pVolumeBitmap->BitmapSize.QuadPart = BitmapSize;
  94. //get the volume bit map
  95. if(ESDeviceIoControl(hVolumeHandle,
  96. FSCTL_GET_VOLUME_BITMAP,
  97. &StartingLcnInputBuffer,
  98. sizeof(STARTING_LCN_INPUT_BUFFER),
  99. pVolumeBitmap,
  100. (DWORD)GlobalSize(hVolumeBitmap),
  101. &BytesReturned,
  102. NULL))
  103. {
  104. //start and the beginning of the disk with 0 free space found
  105. FreeCount = 0;
  106. SearchStartLcn = 0;
  107. //get a pointer that points to the bitmap part of the bitmap
  108. //(past the header)
  109. pBitmap = (PULONG)&pVolumeBitmap->Buffer;
  110. //mark the bit map used for the mft in NTFS
  111. if(IsNtfs)
  112. {
  113. MarkBitMapforNTFS(pBitmap, MftZoneStart, MftZoneEnd);
  114. } //search for free space on the drive starting with lcn 0
  115. while(FreeCount < (LONGLONG)ulFileSize && SearchStartLcn < TotalClusters)
  116. {
  117. FindFreeExtent(
  118. pBitmap, //Volume bit map
  119. TotalClusters, //range end
  120. &SearchStartLcn, //where to start searching
  121. &FreeStartLcn, //first free starting LCN
  122. &FreeCount //cluster count found
  123. );
  124. if(FreeCount > (LONGLONG)ulFileSize)
  125. {
  126. break;
  127. }
  128. }
  129. }
  130. }
  131. }
  132. if(hVolumeBitmap != NULL)
  133. {
  134. GlobalUnlock(hVolumeBitmap);
  135. GlobalFree(hVolumeBitmap);
  136. }
  137. hVolumeBitmap = NULL;
  138. return FreeStartLcn;
  139. }
  140. /*****************************************************************************************************************
  141. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  142. ROUTINE DESCRIPTION:
  143. Update the volume bitmap marking the MFT Zone as used space
  144. INPUT:
  145. PULONG pBitmap The volume bit map
  146. ULONGLONG MftZoneStart Start of the MFT Zone
  147. ULONGLONG MftZoneEnd End of the MFT Zone
  148. RETURN:
  149. PULONG pBitmap Updated bitmap with the MFT Zone marked as used
  150. */
  151. VOID MarkBitMapforNTFS(
  152. IN OUT PULONG pBitmap,
  153. IN ULONGLONG MftZoneStart,
  154. IN ULONGLONG MftZoneEnd
  155. )
  156. {
  157. //0.0E00 Fill the MFT zone with not-free
  158. ULONGLONG Cluster;
  159. if(MftZoneEnd > MftZoneStart)
  160. {
  161. Cluster = MftZoneStart;
  162. while((MODULUSLONGLONGBY32(Cluster) != 0) && (Cluster < MftZoneEnd))
  163. {
  164. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  165. Cluster ++;
  166. }
  167. if(Cluster < MftZoneEnd)
  168. {
  169. while(MftZoneEnd - Cluster >= 32)
  170. {
  171. pBitmap[DIVIDELONGLONGBY32(Cluster)] = 0xffffffff;
  172. Cluster += 32;
  173. }
  174. while(Cluster < MftZoneEnd)
  175. {
  176. pBitmap[DIVIDELONGLONGBY32(Cluster)] |= (1 << (ULONG) MODULUSLONGLONGBY32(Cluster));
  177. Cluster ++;
  178. }
  179. }
  180. }
  181. }
  182. /*****************************************************************************************************************
  183. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  184. ROUTINE DESCRIPTION:
  185. Move a file to a new location to defrag it.
  186. INPUT:
  187. HANDLE hMFTHandle Handle for the MFT
  188. ULONGLONG ulFirstAvailableFreeSpace Cluster location to move the file to
  189. ULONGLONG ulFileSize Size of the file in clusters
  190. ULONGLONG ulStartingVcn What VCN to start with when moving the file
  191. HANDLE hVolumeHandle The handle to the current volume
  192. RETURN:
  193. BOOL returns if the move was successful, TRUE it worked, FALSE it didn't
  194. */
  195. BOOL MoveFileLocation(
  196. IN HANDLE hMFTHandle,
  197. IN ULONGLONG ulFirstAvailableFreeSpace,
  198. IN ULONGLONG ulFileSize,
  199. IN ULONGLONG ulStartingVcn,
  200. IN HANDLE hVolumeHandle
  201. )
  202. {
  203. MOVE_FILE_DATA MoveFileData; //buffer to hold move file data
  204. ULONG BytesReturned = 0; //number of bytes returned from NTControlFile
  205. HANDLE hRetrievalPointersBuffer = NULL; //handle for the retrieval pointers
  206. BOOL bReturnValue = FALSE; //value to return
  207. //
  208. // Open the file
  209. //
  210. if ((hMFTHandle) && (hMFTHandle != INVALID_HANDLE_VALUE)) {
  211. // Initialize the call to the hook to move this file.
  212. MoveFileData.FileHandle = hMFTHandle;
  213. MoveFileData.StartingVcn.QuadPart = ulStartingVcn;
  214. MoveFileData.StartingLcn.QuadPart = ulFirstAvailableFreeSpace;
  215. MoveFileData.ClusterCount = (ULONG)ulFileSize;
  216. bReturnValue = ESDeviceIoControl(hVolumeHandle,
  217. FSCTL_MOVE_FILE,
  218. &MoveFileData,
  219. sizeof(MOVE_FILE_DATA),
  220. NULL,
  221. 0,
  222. &BytesReturned,
  223. NULL);
  224. }
  225. return bReturnValue;
  226. }
  227. //
  228. // Returns true if szName starts with \??\Volume{ or \\?\Volume{
  229. //
  230. BOOL
  231. StartsWithVolumeGuid(IN PCWSTR szName) {
  232. if (!szName) {
  233. return FALSE;
  234. }
  235. if (wcslen(szName) < 49) {
  236. return FALSE;
  237. }
  238. //
  239. // This is ugly, but I'm not using wcsicmp since we don't link
  240. // to msvcrt
  241. //
  242. if ((L'\\' == szName[0]) &&
  243. ((L'\\' == szName[1]) || (L'?' == szName[1])) &&
  244. (L'?' == szName[2]) &&
  245. (L'\\' == szName[3]) &&
  246. ((L'V' == szName[4]) || (L'v' == szName[4])) &&
  247. ((L'O' == szName[5]) || (L'o' == szName[5])) &&
  248. ((L'L' == szName[6]) || (L'l' == szName[6])) &&
  249. ((L'U' == szName[7]) || (L'u' == szName[7])) &&
  250. ((L'M' == szName[8]) || (L'm' == szName[8])) &&
  251. ((L'E' == szName[9]) || (L'e' == szName[9])) &&
  252. (L'{' == szName[10])
  253. ) {
  254. return TRUE;
  255. }
  256. return FALSE;
  257. }
  258. /*****************************************************************************************************************
  259. ROUTINE DESCRIPTION:
  260. Send a pause message to the engine if a snapshot has been created for the
  261. the volume being defragmented.
  262. INPUT:
  263. PWSTR pVolumeName: The volume name of the volume being defragmented:
  264. of the form \\?\Volume{GUID} or \\?\Volume{GUID}\
  265. RETURN:
  266. BOOL returns TRUE
  267. */
  268. BOOL PauseOnVolumeSnapshot(
  269. IN PWSTR pVolumeName
  270. )
  271. {
  272. BOOL bSnapshot = FALSE, bPaused = FALSE;
  273. WCHAR szVolumeName[GUID_LENGTH + 1];
  274. DWORD dwLength = 0, count = 0;
  275. LONG lSnapCapability = 0;
  276. wcsncpy(szVolumeName, pVolumeName, GUID_LENGTH-1);
  277. dwLength = wcslen(szVolumeName);
  278. if (L'\\' != szVolumeName[dwLength]) {
  279. //
  280. // Add a terminating back-slash
  281. //
  282. szVolumeName[dwLength+1] = L'\0';
  283. szVolumeName[dwLength] = L'\\';
  284. }
  285. while ((S_OK == IsVolumeSnapshotted(szVolumeName, &bSnapshot, &lSnapCapability))
  286. && bSnapshot
  287. && (lSnapCapability & VSS_SC_DISABLE_DEFRAG)
  288. ) {
  289. //
  290. // A snapshot is present for this volume. Send a pause message to
  291. // the engine (this updates the UI as well), and idle-wait here.
  292. //
  293. // Note that we're sending the pause message in a loop, since the
  294. // user might hit Resume.
  295. //
  296. PostMessage(hwndMain, WM_COMMAND, ID_PAUSE_ON_SNAPSHOT, 0);
  297. bPaused = TRUE;
  298. if ((bCommandLineMode) && (++count > 10)) {
  299. PostMessage(hwndMain, WM_COMMAND, ID_CONTINUE, 0);
  300. PostMessage(hwndMain, WM_COMMAND, ID_ABORT_ON_SNAPSHOT, 0);
  301. }
  302. Sleep(60000); // check after a minute;
  303. }
  304. if (bPaused) {
  305. //
  306. // If we paused the engine, restart it.
  307. //
  308. PostMessage(hwndMain, WM_COMMAND, ID_CONTINUE, 0);
  309. }
  310. return TRUE;
  311. }
  312. /*****************************************************************************************************************
  313. ROUTINE DESCRIPTION:
  314. Acquire the required privilege (such as the backup privilege)
  315. INPUT:
  316. PCWSTR szPrivilegeName - The privilege to be acquired
  317. RETURN:
  318. TRUE if the privilege could be acquired, FALSE otherwise.
  319. GetLastError will return the error code.
  320. */
  321. BOOL
  322. AcquirePrivilege(
  323. IN CONST PCWSTR szPrivilegeName
  324. )
  325. {
  326. HANDLE hToken = NULL;
  327. BOOL bResult = FALSE;
  328. LUID luid;
  329. TOKEN_PRIVILEGES tNewState;
  330. bResult = OpenProcessToken(GetCurrentProcess(),
  331. MAXIMUM_ALLOWED,
  332. &hToken
  333. );
  334. if (!bResult) {
  335. return FALSE;
  336. }
  337. bResult = LookupPrivilegeValue(NULL, szPrivilegeName, &luid);
  338. if (!bResult) {
  339. CloseHandle(hToken);
  340. return FALSE;
  341. }
  342. tNewState.PrivilegeCount = 1;
  343. tNewState.Privileges[0].Luid = luid;
  344. tNewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  345. //
  346. // We will always call GetLastError below, so clear
  347. // any prior error values on this thread.
  348. //
  349. SetLastError(ERROR_SUCCESS);
  350. bResult = AdjustTokenPrivileges(
  351. hToken, // Token Handle
  352. FALSE, // DisableAllPrivileges
  353. &tNewState, // NewState
  354. (DWORD) 0, // BufferLength
  355. NULL, // PreviousState
  356. NULL // ReturnLength
  357. );
  358. //
  359. // Supposedly, AdjustTokenPriveleges always returns TRUE
  360. // (even when it fails). So, call GetLastError to be
  361. // extra sure everything's cool.
  362. //
  363. if (ERROR_SUCCESS != GetLastError()) {
  364. bResult = FALSE;
  365. }
  366. CloseHandle(hToken);
  367. return bResult;
  368. }