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.

1404 lines
46 KiB

  1. /****************************************************************************************************************
  2. File Name: FsSubs.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. Description: Common file system routines.
  5. /***************************************************************************************************************/
  6. #include "stdafx.h"
  7. #ifdef OFFLINEDK
  8. extern "C"{
  9. #include <stdio.h>
  10. }
  11. #endif
  12. #ifdef BOOTIME
  13. #include "Offline.h"
  14. #else
  15. #include "Windows.h"
  16. #endif
  17. #include <winioctl.h>
  18. extern "C" {
  19. #include "SysStruc.h"
  20. }
  21. #include "ErrMacro.h"
  22. #include "DfrgCmn.h"
  23. #include "DfrgEngn.h"
  24. #include "DfrgRes.h"
  25. #include "FsSubs.h"
  26. #include "Alloc.h"
  27. #include "Message.h"
  28. #include "Devio.h"
  29. #include "IntFuncs.h"
  30. #include "ErrMsg.h"
  31. #include "Message.h"
  32. #include "Logging.h"
  33. #include "GetDfrgRes.h"
  34. #include "getreg.h"
  35. #include "Exclude.h"
  36. #include "FreeSpace.h"
  37. #include "UiCommon.h"
  38. #define THIS_MODULE 'S'
  39. #include "logfile.h"
  40. #ifdef DFRGNTFS
  41. #include "NtfsSubs.h"
  42. #else
  43. #include "FatSubs.h"
  44. #endif
  45. /*****************************************************************************************************************
  46. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  47. ROUTINE DESCRIPTION:
  48. This gets the file system for a drive and returns a numeric value giving which file system it is.
  49. The second version also returns the volume name too.
  50. There are two versions, one for offline DK and one for online and DKMS.
  51. INPUT + OUTPUT:
  52. IN DriveLetter - The drive letter to get the file system of.
  53. OUT pFileSystem - Where to put the ULONG identifying the file system.
  54. OUT pVolumeName - The name of the volume. Must be MAX_PATH length or greater. (only on second version).
  55. GLOBALS:
  56. None.
  57. RETURN:
  58. TRUE - Success.
  59. FALSE - Fatal Error.
  60. */
  61. #ifdef OFFLINEDK
  62. BOOL
  63. GetFileSystem(
  64. IN TCHAR DriveLetter,
  65. OUT PULONG pFileSystem
  66. )
  67. {
  68. TCHAR cDrive[8];
  69. HANDLE hVolume;
  70. NTSTATUS Status;
  71. IO_STATUS_BLOCK IoStatus = {0};
  72. HANDLE hFsAttrInfo = NULL;
  73. FILE_FS_ATTRIBUTE_INFORMATION* pFsAttrInfo = NULL;
  74. BOOL bReturnValue = FALSE; // assume error
  75. EF(AllocateMemory(sizeof(FILE_FS_ATTRIBUTE_INFORMATION)+MAX_PATH, &hFsAttrInfo, (void**)&pFsAttrInfo));
  76. __try {
  77. //0.0E00 Get handle to volume
  78. _stprintf(cDrive, TEXT("\\\\.\\%c:"), DriveLetter);
  79. hVolume = CreateFile(
  80. cDrive,
  81. 0,
  82. FILE_SHARE_READ|FILE_SHARE_WRITE,
  83. NULL,
  84. OPEN_EXISTING,
  85. FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
  86. NULL);
  87. if (hVolume == INVALID_HANDLE_VALUE){
  88. EH(FALSE);
  89. __leave;
  90. }
  91. Status = NtQueryVolumeInformationFile(
  92. hVolume,
  93. &IoStatus,
  94. pFsAttrInfo,
  95. sizeof(FILE_FS_ATTRIBUTE_INFORMATION)+50,
  96. FileFsAttributeInformation);
  97. CloseHandle(hVolume);
  98. if (!NT_SUCCESS(Status)){
  99. EH(FALSE);
  100. __leave;
  101. }
  102. //Add a null terminator.
  103. pFsAttrInfo->FileSystemName[pFsAttrInfo->FileSystemNameLength] = 0;
  104. //1.0E00 Compare the file system name against the known types and return an appropriate value.
  105. // Note: FileSystemName in this structure is always WideChar
  106. if(_tcscmp(pFsAttrInfo->FileSystemName, TEXT("NTFS")) == 0){
  107. *pFileSystem = FS_NTFS;
  108. }
  109. else if(_tcscmp(pFsAttrInfo->FileSystemName, TEXT("FAT")) == 0){
  110. *pFileSystem = FS_FAT;
  111. }
  112. else if(_tcscmp(pFsAttrInfo->FileSystemName, TEXT("FAT32")) == 0){
  113. *pFileSystem = FS_FAT32;
  114. }
  115. else {
  116. *pFileSystem = FS_NONE;
  117. }
  118. bReturnValue = TRUE; // all is ok
  119. }
  120. __finally {
  121. if (hFsAttrInfo) {
  122. EH_ASSERT(GlobalUnlock(hFsAttrInfo) == FALSE);
  123. EH_ASSERT(GlobalFree(hFsAttrInfo) == NULL);
  124. }
  125. }
  126. return bReturnValue;
  127. }
  128. #else
  129. BOOL
  130. GetFileSystem(
  131. IN PTCHAR volumeName,
  132. OUT PULONG pFileSystem,
  133. OUT TCHAR* pVolumeLabel
  134. )
  135. {
  136. TCHAR cVolumeLabelBuffer[100];
  137. TCHAR cFileSystemNameBuffer[16];
  138. TCHAR cTmpVolumeName[GUID_LENGTH+1];
  139. //TCHAR cDrive[8];
  140. //1.0E00 Get the file system name in text.
  141. //_stprintf(cDrive, TEXT("%c:\\"), DriveLetter);
  142. // the UNC version of the vol name needs an additional backslash
  143. // GUID version must look like: "\\?\Volume{06bee7c1-82cf-11d2-afb2-000000000000}\\"
  144. _stprintf(cTmpVolumeName, TEXT("%s\\"), volumeName);
  145. BOOL retCode = GetVolumeInformation(
  146. cTmpVolumeName,
  147. cVolumeLabelBuffer,
  148. sizeof(cVolumeLabelBuffer) / sizeof(TCHAR),
  149. NULL,
  150. NULL,
  151. NULL,
  152. cFileSystemNameBuffer,
  153. 15);
  154. if (!retCode){
  155. Message(TEXT("GetFileSystem() failed"), GetLastError(), volumeName);
  156. EF(FALSE);
  157. }
  158. //Store the name of the volume.
  159. _tcscpy(pVolumeLabel, cVolumeLabelBuffer);
  160. //1.0E00 Compare the file system name against the known types and return an appropriate value.
  161. if(lstrcmp(cFileSystemNameBuffer, TEXT("NTFS")) == 0){
  162. *pFileSystem = FS_NTFS;
  163. }
  164. else if(lstrcmp(cFileSystemNameBuffer, TEXT("FAT")) == 0){
  165. *pFileSystem = FS_FAT;
  166. }
  167. else if(lstrcmp(cFileSystemNameBuffer, TEXT("FAT32")) == 0){
  168. *pFileSystem = FS_FAT32;
  169. }
  170. else{
  171. *pFileSystem = FS_NONE;
  172. }
  173. return TRUE;
  174. }
  175. #endif
  176. /*****************************************************************************************************************
  177. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  178. ROUTINE DESCRIPTION:
  179. Sometimes retrieval pointers are stored that list two extents that are in fact contiguous. This function spots
  180. this occurrence and "collapses" the extent list so it only shows one large extent in place of two smaller
  181. extents. Also, compressed files have two retrieval pointers per extent. The first contains the actual extent
  182. on the disk, the second contains the virtual extent, which contains only a count of how many clusters the
  183. extent would be if it weren't compressed. The second extents are called virtual extents and contain a starting
  184. Lcn of 0xFFFFFFFFFFFFFFFF to identify them as virtual. While these virtual extents are not removed from our
  185. extent list, they aren't counted as separate extents in the file.
  186. INPUT + OUTPUT:
  187. None.
  188. GLOBALS:
  189. IN OUT VolData.NumberOfExtents - The number of extents in the extent list.
  190. IN OUT VolData.pExtentList - The extent list.
  191. OUT VolData.NumberOfFragments - The number of fragments in this file.
  192. OUT VolData.ExtentsState - Whether or not the file is contiguous or fragmented.
  193. RETURN:
  194. TRUE - Success.
  195. FALSE - Fatal Error.
  196. */
  197. BOOL
  198. CollapseExtentList(
  199. EXTENT_LIST_DATA* pExtentData
  200. )
  201. {
  202. LONGLONG Extent = 0;
  203. LONGLONG Dest;
  204. UINT i;
  205. FILE_EXTENT_HEADER* pFileExtentHeader;
  206. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  207. STREAM_EXTENT_HEADER* pNextStreamExtentHeader;
  208. UCHAR* pThisStreamEnd;
  209. EXTENT_LIST* pExtents;
  210. UCHAR* pExtentListEnd;
  211. pFileExtentHeader = (FILE_EXTENT_HEADER*)pExtentData->pExtents;
  212. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pExtentData->pExtents + sizeof(FILE_EXTENT_HEADER));
  213. pNextStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  214. pExtentListEnd = (UCHAR*)pExtentData->pExtents + pExtentData->ExtentListSize;
  215. //Note that the old excess extents count might be invalid when we recount the extents after collapsing them.
  216. pFileExtentHeader->ExcessExtents = 0;
  217. //Loop through each stream. We're going collapse extents stream by stream.
  218. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++){
  219. pStreamExtentHeader->ExcessExtents = 0;
  220. //This count will only include normal extents. It will not count virtual extents as separate.
  221. if(pStreamExtentHeader->ExtentCount){
  222. //This stream has an extent in it, therefore it has at least one fragment.
  223. VolData.NumberOfFragments = pFileExtentHeader->NumberOfStreams;
  224. }
  225. //Get a pointer to this stream's extents.
  226. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  227. //0.1E00 Collapse the extent list by combining adjacent extents
  228. //0.1E00 Go through each extent and see if it is really adjacent to the next and combine them if they are.
  229. for(Dest = Extent = 1; Extent < pStreamExtentHeader->ExtentCount; Extent ++){
  230. //0.1E00 If this extent is adjacent to the last extent
  231. if(pExtents[Extent - 1].StartingLcn +
  232. pExtents[Extent - 1].ClusterCount ==
  233. pExtents[Extent].StartingLcn){
  234. //0.1E00 Combine this extent into the last and don't move the dest index
  235. pExtents[Dest - 1].ClusterCount += pExtents[Extent].ClusterCount;
  236. }else{
  237. //0.1E00 Copy this extent to the dest index and bump the dest index
  238. pExtents[Dest].StartingLcn = pExtents[Extent].StartingLcn;
  239. pExtents[Dest ++].ClusterCount = pExtents[Extent].ClusterCount;
  240. }
  241. }
  242. //0.1E00 If we found some adjacent extents, the Dest count will be less than the previous count for the number of extents.
  243. //0.1E00 Update the number of extents in this file.
  244. if(Dest != pStreamExtentHeader->ExtentCount){
  245. pStreamExtentHeader->ExtentCount = (DWORD)Dest;
  246. }
  247. //0.1E00 Determine the number of fragments in this file
  248. //0.1E00 The while loop below will count the number of fragments in this file by excluding the virtual extents, and not counting adjacent extents.
  249. //0.1E00 First get the first extent that isn't virtual -- Viz, break out of this loop as soon as we find an extent
  250. //0.1E00 whose starting lcn is not 0xFFFFFFFFFFFFFFFF.
  251. for(Extent = 0;
  252. (Extent < pStreamExtentHeader->ExtentCount) &&
  253. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  254. Extent ++);
  255. //0.1E00 Find the second extent that isn't virtual.
  256. for(Dest = Extent ++;
  257. (Extent < pStreamExtentHeader->ExtentCount) &&
  258. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  259. Extent ++);
  260. //0.1E00 Count all the extents as fragments if they are not adjacent and non-virtual.
  261. while(Extent < pStreamExtentHeader->ExtentCount){
  262. if ((VolData.BootOptimizeEndClusterExclude) &&
  263. (pExtents[Extent].StartingLcn < VolData.BootOptimizeEndClusterExclude) &&
  264. ((pExtents[Extent].StartingLcn + pExtents[Extent].ClusterCount) > VolData.BootOptimizeBeginClusterExclude)
  265. ) {
  266. //
  267. // Set the flag to indicate that this file is in the region
  268. // marked for boot-optimise files. Note that if
  269. // VolData.BootOptimizeEndClusterExclude is 0, we don't care
  270. // about this flag and don't perform the rest of the check to
  271. // see if this is in the boot-optimise region
  272. //
  273. VolData.bInBootExcludeZone = TRUE;
  274. }
  275. //0.1E00 If these two extents are not adjacent, count the fragment.
  276. if(pExtents[Extent].StartingLcn !=
  277. pExtents[Dest].StartingLcn +
  278. pExtents[Dest].ClusterCount){
  279. pFileExtentHeader->ExcessExtents++;
  280. pStreamExtentHeader->ExcessExtents++;
  281. VolData.NumberOfFragments ++;
  282. }
  283. //0.1E00 Look for the next non-virtual extent.
  284. for(Dest = Extent ++;
  285. (Extent < pStreamExtentHeader->ExtentCount) &&
  286. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  287. Extent ++);
  288. }
  289. //Note the end of this stream.
  290. pThisStreamEnd = (UCHAR*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  291. //Note how much the whole extent list is shrinking. (If the size of this stream didn't change, this is a simple -= 0).
  292. pExtentData->ExtentListSize -= (DWORD)((UCHAR*)pNextStreamExtentHeader - (UCHAR*)pThisStreamEnd);
  293. //If there is another stream after the current one, then move it up against this stream.
  294. //Don't do this move if nothing has been changed in the previous stream: the dest and source for the move are equal.
  295. if(i < pFileExtentHeader->NumberOfStreams-1 && (char*)pThisStreamEnd != (char*)pNextStreamExtentHeader){
  296. //Move the later streams up to butt against the new end of the extent list.
  297. MoveMemory(pThisStreamEnd, (UCHAR*)pNextStreamExtentHeader, (DWORD)((UCHAR*)pExtentListEnd-(UCHAR*)pNextStreamExtentHeader));
  298. //Reset the pointer to the end of the extent list to point to the new end of the extent list.
  299. pExtentListEnd = (UCHAR*)pExtentData->pExtents + pExtentData->ExtentListSize;
  300. }
  301. //Go to the next stream.
  302. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  303. if((ULONG_PTR)pStreamExtentHeader < (ULONG_PTR)pExtentListEnd){
  304. //Only get a pointer to the next next stream if we haven't gone off the end of the known extent list yet.
  305. pNextStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  306. }
  307. }
  308. //0.1E00 If there is only one fragment per stream, then the file is contiguous, otherwise it is fragmented.
  309. VolData.bFragmented = (VolData.NumberOfFragments <= pFileExtentHeader->NumberOfStreams) ? FALSE : TRUE;
  310. //0.1E00 Get the lowest Lcn of the file. This is used in our algorithms to determine which files to move.
  311. EF(GetLowestStartingLcn(&VolData.StartingLcn, pFileExtentHeader));
  312. return TRUE;
  313. }
  314. BOOL
  315. CountStreamExtentsAndClusters(
  316. DWORD dwStreamNumber,
  317. LONGLONG* pExcessExtents,
  318. LONGLONG* pClusters
  319. )
  320. {
  321. /* LONGLONG Extent = 0;
  322. LONGLONG Dest;
  323. UINT i;
  324. FILE_EXTENT_HEADER* pFileExtentHeader;
  325. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  326. EXTENT_LIST* pExtents;
  327. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  328. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  329. //Check for a valid stream number.
  330. EF_ASSERT(dwStreamNumber<pFileExtentHeader->TotalNumberOfStreams);
  331. *pExcessExtents = 0;
  332. *pClusters = 0;
  333. // *pRealClusters = 0;
  334. //Find the stream we were working on before.
  335. //Loop through until we hit the one we were on, each time bumping the stream header to the next stream.
  336. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++){
  337. if(pStreamExtentHeader->StreamNumber != dwStreamNumber){
  338. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  339. }
  340. }
  341. //Get a pointer to this stream's extents.
  342. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  343. //0.1E00 Determine the number of fragments and real clusters in this file
  344. //0.1E00 The while loop below will count the number of fragments in this file by excluding the virtual extents, and not counting adjacent extents.
  345. //0.1E00 First get the first extent that isn't virtual -- Viz, break out of this loop as soon as we find an extent
  346. //0.1E00 whose starting lcn is not 0xFFFFFFFFFFFFFFFF.
  347. for(Extent = 0;
  348. (Extent < pStreamExtentHeader->ExtentCount) &&
  349. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  350. Extent ++);
  351. //0.1E00 Find the second extent that isn't virtual.
  352. for(Dest = Extent ++;
  353. (Extent < pStreamExtentHeader->ExtentCount) &&
  354. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  355. Extent ++);
  356. //0.1E00 Count all the extents as fragments if they are not adjacent and non-virtual.
  357. while(Extent < pStreamExtentHeader->ExtentCount){
  358. // //Keep a running total of the number of real clusters.
  359. // *pRealClusters += pExtents[Dest].ClusterCount;
  360. //0.1E00 If these two extents are not adjacent, count the fragment.
  361. if(pExtents[Extent].StartingLcn !=
  362. pExtents[Dest].StartingLcn +
  363. pExtents[Dest].ClusterCount){
  364. pExcessExtents++;
  365. }
  366. //0.1E00 Look for the next non-virtual extent.
  367. for(Dest = Extent ++;
  368. (Extent < pStreamExtentHeader->ExtentCount) &&
  369. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  370. Extent ++);
  371. }
  372. // //Don't forget to add the last extent's cluster count to the total of real clusters.
  373. // *pRealClusters += pExtents[Extent].ClusterCount;
  374. //Now loop through and count the number of virtual clusters in the file.
  375. for(Extent=0; Extent<pStreamExtentHeader->ExtentCount; Extent++){
  376. *pClusters += pExtents[Extent].ClusterCount;
  377. }
  378. */ return TRUE;
  379. }
  380. /*****************************************************************************************************************
  381. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  382. ROUTINE DESCRIPTION:
  383. Determine which is the lowest Lcn in the file, but not one that's in the MFT zone unless the entire
  384. file is in the MFT zone. Make sense?
  385. INPUT + OUTPUT:
  386. OUT pStartingLcn - The lowest Lcn in the file
  387. GLOBALS:
  388. IN VolData.pExtentList - The extent list for the file.
  389. IN VolData.NumberOfExtents - The number of extents in the extent list.
  390. IN VolData.NumberOfFragments - The number of fragments in the file.
  391. RETURN:
  392. TRUE - Success.
  393. FALSE - Fatal Error.
  394. */
  395. BOOL
  396. GetLowestStartingLcn(
  397. OUT LONGLONG* pStartingLcn,
  398. FILE_EXTENT_HEADER* pFileExtentHeader
  399. )
  400. {
  401. LONGLONG Extent = 1;
  402. LONGLONG LowestStartingLcn;
  403. LONGLONG Lcn;
  404. UINT i;
  405. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  406. EXTENT_LIST* pExtents;
  407. EF_ASSERT(pStartingLcn);
  408. //Initialize the return StartingLcn.
  409. *pStartingLcn = 0;
  410. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pFileExtentHeader + sizeof(FILE_EXTENT_HEADER));
  411. //Loop through each stream. We're going to return the lowest starting lcn for any stream.
  412. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++){
  413. //Get a pointer to this stream's extents.
  414. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  415. //0.0E00 Find the first non-virtual extent
  416. for(Extent = 0;
  417. (Extent < pStreamExtentHeader->ExtentCount) &&
  418. (pExtents[Extent].StartingLcn == 0xFFFFFFFFFFFFFFFF);
  419. Extent ++);
  420. //0.0E00 Don't suicide on compressed files of all zeros which use no clusters on the volume
  421. if(Extent >= pStreamExtentHeader->ExtentCount){
  422. //Go to the next stream.
  423. continue;
  424. }
  425. //0.0E00 Use first extent as reference Lcn
  426. LowestStartingLcn = pExtents[Extent ++].StartingLcn;
  427. //0.1E00 Only flip through the extents of the file if it's fragmented.
  428. if(pStreamExtentHeader->ExtentCount > 1){
  429. //0.0E00 Scan the entire extent list
  430. for(; Extent < pStreamExtentHeader->ExtentCount; Extent ++){
  431. //0.0E00 Ignore virtual extents
  432. if(pExtents[Extent].StartingLcn != 0xFFFFFFFFFFFFFFFF){
  433. Lcn = pExtents[Extent].StartingLcn;
  434. //0.1E00 The following only records the Lcn if it is the lowest number unless the first Lcn is in the MFT zone.
  435. //0.0E00 If this extent is the lowest so far, record it
  436. //Or if this extent is in the MFT zone.
  437. if((Lcn < LowestStartingLcn) ||
  438. ((VolData.FileSystem == FS_NTFS) && (LowestStartingLcn >= VolData.MftZoneStart) &&
  439. (LowestStartingLcn < VolData.MftZoneEnd))){
  440. //If this Lcn is not in the MFT zone, then record it as the earliest Starting Lcn.
  441. if((Lcn < VolData.MftZoneStart) ||
  442. (Lcn >= VolData.MftZoneEnd)){
  443. LowestStartingLcn = Lcn;
  444. }
  445. }
  446. }
  447. }
  448. }
  449. //Go to the next stream.
  450. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  451. }
  452. //0.1E00 Return the Lcn we found.
  453. *pStartingLcn = LowestStartingLcn;
  454. return TRUE;
  455. }
  456. /*****************************************************************************************************************
  457. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  458. ROUTINE DESCRIPTION:
  459. INPUT + OUTPUT:
  460. None.
  461. GLOBALS:
  462. VolData
  463. RETURN:
  464. TRUE = Success
  465. FALSE = Failure
  466. */
  467. #if defined(DFRGFAT) || defined(DFRGNTFS)
  468. BOOL
  469. FillMostFraggedList(CFraggedFileList &fraggedFileList, IN CONST BOOL fAnalyseOnly)
  470. {
  471. UINT Index = 0;
  472. LONGLONG LeastExtents = 0;
  473. LONGLONG Extents = 0;
  474. #ifdef DFRGNTFS
  475. // start with the first entry in the moveable file list
  476. PFILE_LIST_ENTRY pFileListEntry = NULL;
  477. ULONG numFragmentedFiles = 0;
  478. if (fAnalyseOnly) {
  479. if (RtlNumberGenericTableElementsAvl(&VolData.FragmentedFileTable) > 0) {
  480. pFileListEntry = (PFILE_LIST_ENTRY) LastEntry(&VolData.FragmentedFileTable);
  481. }
  482. while ((pFileListEntry) && (numFragmentedFiles < 30)) {
  483. // Get the file record so we can get the file name.
  484. VolData.FileRecordNumber = pFileListEntry->FileRecordNumber;
  485. if (!GetInUseFrs(VolData.hVolume,
  486. &VolData.FileRecordNumber,
  487. (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord,
  488. (ULONG)VolData.BytesPerFRS)) {
  489. pFileListEntry = (PFILE_LIST_ENTRY) PreviousEntry(pFileListEntry);
  490. continue;
  491. }
  492. // Ensure that it is the correct file record
  493. // if not, spit out a message and continue
  494. if (VolData.FileRecordNumber != pFileListEntry->FileRecordNumber){
  495. pFileListEntry = (PFILE_LIST_ENTRY) PreviousEntry(pFileListEntry);
  496. continue;
  497. }
  498. // Ensure that it is the correct file record
  499. if (VolData.FileRecordNumber == pFileListEntry->FileRecordNumber){
  500. // Get the file name to add to the list
  501. if (GetNtfsFilePath() && (VolData.vFileName.GetLength() > 0)){
  502. // Get the extent list in a new unit of time to get its size.
  503. if (GetExtentList(DEFAULT_STREAMS, NULL)){
  504. // add the file to the fragged file list
  505. fraggedFileList.Add(
  506. VolData.vFileName.GetBuffer(),
  507. VolData.FileSize,
  508. pFileListEntry->ExcessExtentCount+1);
  509. ++numFragmentedFiles;
  510. }
  511. }
  512. }
  513. pFileListEntry = (PFILE_LIST_ENTRY) PreviousEntry(pFileListEntry);
  514. }
  515. return TRUE;
  516. }
  517. // Search through the moveable filelist for most fragmented files.
  518. for(pFileListEntry = (PFILE_LIST_ENTRY) RtlEnumerateGenericTableAvl(&VolData.FragmentedFileTable, TRUE);
  519. pFileListEntry;
  520. pFileListEntry = (PFILE_LIST_ENTRY) RtlEnumerateGenericTableAvl(&VolData.FragmentedFileTable, FALSE)) {
  521. // Check to see if this file is more fragmented than the least "most fragged" file.
  522. // Directories okay on VER 5
  523. if (pFileListEntry->ExcessExtentCount+1 > fraggedFileList.GetMinExtentCount()) {
  524. // Get the file record so we can get the file name.
  525. VolData.FileRecordNumber = pFileListEntry->FileRecordNumber;
  526. if (!GetInUseFrs(VolData.hVolume,
  527. &VolData.FileRecordNumber,
  528. (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord,
  529. (ULONG)VolData.BytesPerFRS)) {
  530. continue;
  531. }
  532. // Ensure that it is the correct file record
  533. // if not, spit out a message and continue
  534. if (VolData.FileRecordNumber != pFileListEntry->FileRecordNumber){
  535. continue;
  536. }
  537. // Get the NTFS file's name.
  538. if (GetNtfsFilePath()&& (VolData.vFileName.GetLength() > 0)){
  539. // Get the extent list in a new unit of time to get its size.
  540. if (GetExtentList(DEFAULT_STREAMS, NULL)){
  541. // add the file to the fragged file list
  542. fraggedFileList.Add(
  543. VolData.vFileName.GetBuffer(),
  544. VolData.FileSize,
  545. pFileListEntry->ExcessExtentCount+1);
  546. }
  547. }
  548. }
  549. #elif DFRGFAT
  550. // start with the first entry in the moveable file list
  551. FILE_LIST_ENTRY *pFileListEntry = VolData.pMoveableFileList;
  552. BOOL VolFragmented = FALSE;
  553. // Search through the moveable filelist for most fragmented files.
  554. for(UINT i = 0; i < VolData.MoveableFileListEntries; i++){
  555. // end of list?
  556. if(pFileListEntry->FileRecordNumber == 0){
  557. break;
  558. }
  559. #ifdef VER4
  560. // Check to see if this file is more fragmented than the
  561. // least most fragged file, and not a directory.
  562. if((!(pFileListEntry->Flags & FLE_DIRECTORY) &&
  563. (pFileListEntry->ExcessExtentCount+1 > LeastExtents)) &&
  564. (pFileListEntry->ExcessExtentCount > 0)) {
  565. #else
  566. // Check to see if this file is more fragmented than the least "most fragged" file.
  567. // Directories okay on VER 5
  568. if(pFileListEntry->ExcessExtentCount+1 > LeastExtents &&
  569. pFileListEntry->ExcessExtentCount > 0) {
  570. #endif
  571. VolData.FileRecordNumber = pFileListEntry->FileRecordNumber;
  572. VolData.vFileName = &VolData.pNameList[pFileListEntry->FileRecordNumber];
  573. // open the file to see if it still exists!
  574. if(OpenFatFile()){
  575. // Get the extent list in a new unit of time to get its size.
  576. if (GetExtentList(DEFAULT_STREAMS, NULL)){
  577. // add the file to the fragged file list
  578. fraggedFileList.Add(
  579. &VolData.pNameList[pFileListEntry->FileRecordNumber],
  580. VolData.FileSize,
  581. pFileListEntry->ExcessExtentCount+1);
  582. }
  583. }
  584. }
  585. pFileListEntry++;
  586. #endif
  587. }
  588. return TRUE;
  589. }
  590. #endif // #if defined(DFRGFAT) || defined(DFRGNTFS)
  591. /*****************************************************************************************************************
  592. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  593. ROUTINE DESCRIPTION:
  594. INPUT + OUTPUT:
  595. None.
  596. GLOBALS:
  597. RETURN:
  598. TRUE = The volume is dirty.
  599. FALSE = The volume is not dirty.
  600. */
  601. BOOL
  602. IsVolumeDirty(
  603. void
  604. )
  605. {
  606. ULONG uDirty=0;
  607. DWORD BytesReturned = 0;
  608. #if defined(DFRGFAT) || defined(DFRGNTFS) // get a good link when used with the UI
  609. EF(ESDeviceIoControl(VolData.hVolume,
  610. FSCTL_IS_VOLUME_DIRTY,
  611. NULL,
  612. 0,
  613. &uDirty,
  614. sizeof(uDirty),
  615. &BytesReturned,
  616. NULL));
  617. #endif
  618. if (uDirty & (VOLUME_DIRTY | VOLUME_UPGRADE_ON_MOUNT)) {
  619. return TRUE;
  620. }
  621. return FALSE;
  622. }
  623. /*****************************************************************************************************************
  624. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  625. ROUTINE DESCRIPTION:
  626. INPUT + OUTPUT:
  627. None.
  628. GLOBALS:
  629. RETURN:
  630. TRUE = Drive letter was found
  631. FALSE = Drive letter was not found
  632. */
  633. #ifndef VER4
  634. BOOL GetDriveLetterByGUID(
  635. PTCHAR volumeName,
  636. TCHAR &driveLetter
  637. )
  638. {
  639. TCHAR cDrive[10];
  640. TCHAR tmpVolumeName[GUID_LENGTH];
  641. TCHAR drive;
  642. BOOL isOk;
  643. cDrive[1] = L':';
  644. cDrive[2] = L'\\';
  645. cDrive[3] = 0;
  646. for (drive = L'A'; drive <= L'Z'; drive++) {
  647. cDrive[0] = drive;
  648. // get the VolumeName based on the mount point or drive letter
  649. isOk = GetVolumeNameForVolumeMountPoint(cDrive, tmpVolumeName, GUID_LENGTH);
  650. if (isOk) {
  651. // did we get a match on the GUID?
  652. if (_tcsncmp(tmpVolumeName, volumeName, 48) == 0) { // ignore whether backslash is there
  653. driveLetter = drive;
  654. return TRUE;
  655. }
  656. }
  657. }
  658. return FALSE;
  659. }
  660. #endif // #ifndef VER4
  661. //////////////////////////////////////////////////////////////////////////
  662. // Finds all the spots where a volume is mounted
  663. // by searching all the drive letters for mount points that they support
  664. // and comparing the volume GUID that is mounted there to the volume GUID we are
  665. // interested in. When the GUIDs match, we have found a mount point for this volume.
  666. // Is that clear?
  667. //
  668. #ifndef VER4
  669. void GetVolumeMountPointList(
  670. PWSTR volumeName, // guid
  671. VString mountPointList[MAX_MOUNT_POINTS],
  672. UINT &mountPointCount
  673. )
  674. {
  675. VString cDrive = TEXT("x:\\");
  676. BOOL mountPointFound = FALSE;
  677. TCHAR drive;
  678. // clear out the mount point list
  679. for (UINT i=0; i<MAX_MOUNT_POINTS; i++){
  680. mountPointList[i].Empty();
  681. }
  682. mountPointCount = 0;
  683. for (drive = L'A'; drive <= L'Z'; drive++) {
  684. if (IsValidVolume(drive)){
  685. cDrive.GetBuffer()[0] = drive;
  686. mountPointFound =
  687. (GetMountPointList(cDrive, volumeName, mountPointList, mountPointCount) || mountPointFound);
  688. }
  689. }
  690. }
  691. #endif // #ifndef VER4
  692. //////////////////////////////////////////////////////////////////////////
  693. // gets all the points where the drive is mounted
  694. //
  695. #ifndef VER4
  696. #define MAX_UNICODE_PATH 32000
  697. BOOL GetMountPointList(
  698. VString Name, // path to a mount point (start with a drive letter)
  699. PWSTR VolumeName, // guid of volume in question
  700. VString mountPointList[MAX_MOUNT_POINTS],
  701. UINT &mountPointCount
  702. )
  703. {
  704. BOOL b;
  705. HANDLE h;
  706. TCHAR tmpVolumeName[GUID_LENGTH];
  707. PWSTR volumeMountPoint;
  708. VString mountPointPath;
  709. BOOL r = FALSE;
  710. if (mountPointCount == MAX_MOUNT_POINTS)
  711. return FALSE; // FALSE = no mount points found
  712. // get the VolumeName based on the mount point or drive letter
  713. b = GetVolumeNameForVolumeMountPoint(Name.GetBuffer(), tmpVolumeName, GUID_LENGTH);
  714. if (!b) {
  715. return r;
  716. }
  717. // Is this volume mounted at this mount point (compare the GUIDs)?
  718. if (!_tcsncmp(tmpVolumeName, VolumeName, 48)) { // only 48 incase of a trailing backslash
  719. r = TRUE;
  720. mountPointList[mountPointCount++] = Name; // save this as a mount point
  721. if (mountPointCount == MAX_MOUNT_POINTS)
  722. return TRUE;
  723. }
  724. // create a buffer large enough to hold a really huge moint point path
  725. // in case the FindFirstVolumeMountPoint() function is changed to return
  726. // long mount point names
  727. volumeMountPoint = (PWSTR) new TCHAR[MAX_UNICODE_PATH]; // 32000
  728. EF_ASSERT(volumeMountPoint);
  729. // find out where else this volume is mounted (other than at a drive letter)
  730. // as of 9/28/98, this function does not return mount points that are longer than MAX_PATH
  731. h = FindFirstVolumeMountPoint(tmpVolumeName, volumeMountPoint, MAX_UNICODE_PATH);
  732. if (h == INVALID_HANDLE_VALUE) {
  733. delete [] volumeMountPoint;
  734. return r;
  735. }
  736. for (;;) {
  737. mountPointPath = Name;
  738. mountPointPath += volumeMountPoint;
  739. if (mountPointPath.IsEmpty() == FALSE &&
  740. GetMountPointList(mountPointPath, VolumeName, mountPointList, mountPointCount)) {
  741. r = TRUE;
  742. }
  743. b = FindNextVolumeMountPoint(h, volumeMountPoint, MAX_UNICODE_PATH);
  744. if (!b) {
  745. break;
  746. }
  747. }
  748. delete [] volumeMountPoint;
  749. FindVolumeMountPointClose(h);
  750. return r;
  751. }
  752. #endif // #ifndef VER4
  753. #ifndef VER4
  754. void FormatDisplayString(
  755. TCHAR driveLetter,
  756. PTCHAR volumeLabel,
  757. VString mountPointList[MAX_MOUNT_POINTS],
  758. UINT mountPointCount,
  759. PTCHAR displayLabel
  760. )
  761. {
  762. // create the volume label display string
  763. if (driveLetter){
  764. if(_tcslen(volumeLabel) == 0){
  765. _stprintf(displayLabel, TEXT("(%c:)"), driveLetter);
  766. }
  767. else{
  768. // "<volume label> (C:)"
  769. _stprintf(displayLabel, TEXT("%s (%c:)"), volumeLabel, driveLetter);
  770. }
  771. }
  772. else if (_tcslen(volumeLabel) > 0){ // use the label only if that's what we have
  773. _tcscpy(displayLabel, volumeLabel);
  774. }
  775. else if (mountPointCount > 0){ // no drive letter or label, use the first mount point
  776. // start off with "Mounted Volume: "
  777. LoadString(GetDfrgResHandle(), IDS_MOUNTED_VOLUME, displayLabel, 50);
  778. _tcscat(displayLabel, TEXT(": "));
  779. // concat the first mount point
  780. if (mountPointList[0].GetLength() > 50) {
  781. _tcsnccat(displayLabel, mountPointList[0].GetBuffer(), 50);
  782. _tcscat(displayLabel, TEXT("..."));
  783. }
  784. else {
  785. _tcscat(displayLabel, mountPointList[0].GetBuffer());
  786. }
  787. }
  788. else { // no drive letter or label or mount point
  789. LoadString(GetDfrgResHandle(), IDS_UNMOUNTED_VOLUME, displayLabel, 50);
  790. }
  791. }
  792. #endif // #ifndef VER4
  793. /*****************************************************************************************************************
  794. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  795. ROUTINE DESCRIPTION:
  796. Detemines if a volume is FAT12 or not (ok to send the a NTFS volume name)
  797. INPUT + OUTPUT:
  798. Volume name (UNC or GUID format)
  799. GLOBALS:
  800. None.
  801. RETURN:
  802. TRUE - Volume is a FAT12
  803. FALSE - Volume is not a FAT12
  804. */
  805. BOOL
  806. IsFAT12Volume(
  807. PTCHAR volumeName
  808. )
  809. {
  810. DWORD fileSystem;
  811. TCHAR volumeLabel[100];
  812. DWORD SectorsPerCluster = 0;
  813. DWORD BytesPerSector = 0;
  814. DWORD FreeClusters = 0;
  815. DWORD TotalNumberOfClusters = 0;
  816. NTSTATUS Status;
  817. IO_STATUS_BLOCK IoStatus = {0};
  818. FILE_FS_SIZE_INFORMATION FsSizeBuf = {0};
  819. #ifdef OFFLINEDK
  820. //0.1E00 Get the file system on this drive.
  821. EF(GetFileSystem(volumeName, &fileSystem));
  822. if (fileSystem== FS_FAT32){
  823. _tprintf(TEXT("\nError - FAT32 is not supported.\n")); // todo Is this still true?
  824. return FALSE;
  825. }
  826. #else
  827. //0.1E00 Get the file system on this drive.
  828. EF(GetFileSystem(volumeName, &fileSystem, volumeLabel));
  829. #endif
  830. //1.0E00 Check to see if this is anything other than FAT or FAT32, and if so, bail out.
  831. if(fileSystem != FS_FAT && fileSystem != FS_FAT32){
  832. return FALSE;
  833. }
  834. // if the last char is a \, null it out
  835. TCHAR tmpVolumeName[GUID_LENGTH+1];
  836. _tcscpy(tmpVolumeName, volumeName);
  837. if (tmpVolumeName[_tcslen(tmpVolumeName)-1] == L'\\'){
  838. tmpVolumeName[_tcslen(tmpVolumeName)-1] = NULL;
  839. }
  840. //bug #120872 sks
  841. _stprintf(tmpVolumeName,TEXT("%s%s"),VolData.cVolumeName,TEXT("\\"));
  842. if(!GetDiskFreeSpace(
  843. tmpVolumeName,
  844. &SectorsPerCluster,
  845. &BytesPerSector,
  846. &FreeClusters,
  847. &TotalNumberOfClusters
  848. ))
  849. {
  850. EF_ASSERT(FALSE);
  851. }
  852. if(TotalNumberOfClusters < 4087){
  853. return TRUE;
  854. }
  855. return FALSE;
  856. }
  857. BOOL IsVolumeRemovable(PTCHAR volumeName)
  858. {
  859. if (GetDriveType(volumeName) == DRIVE_REMOVABLE){
  860. return TRUE;
  861. }
  862. return FALSE;
  863. }
  864. // overloaded version for non-guid versions
  865. void FormatDisplayString(
  866. TCHAR driveLetter,
  867. PTCHAR volumeLabel,
  868. PTCHAR displayLabel
  869. )
  870. {
  871. // create the volume label display string
  872. if(_tcslen(volumeLabel) == 0){
  873. _stprintf(displayLabel, TEXT("(%c:)"), driveLetter);
  874. }
  875. else{
  876. // "<volume label> (C:)"
  877. _stprintf(displayLabel, TEXT("%s (%c:)"), volumeLabel, driveLetter);
  878. }
  879. }
  880. // This strips the volume ID prefix (guid or drive letter)
  881. // and compressed the file name down to 50 characters
  882. BOOL ESICompressFilePath(
  883. IN PTCHAR inFilePath,
  884. OUT PTCHAR outFilePath
  885. )
  886. {
  887. PTCHAR pStartPath = NULL;
  888. PTCHAR pFileNameStart = NULL;
  889. const UINT MAXWIDTH = 50;
  890. if (inFilePath == NULL){
  891. _tcscpy(outFilePath, TEXT("(NULL)"));
  892. return FALSE;
  893. }
  894. // assume that we are prefixed with a GUID
  895. pStartPath = _tcschr(inFilePath, L'}');
  896. if (pStartPath){
  897. pStartPath++; // bump by one to get past the "}"
  898. }
  899. else {
  900. // if that failed, then we will strip off the drive letter
  901. // stuff (e.g. \\.\x:\)
  902. pStartPath = &inFilePath[6];
  903. }
  904. // file name starts at last backslash
  905. pFileNameStart = _tcsrchr(inFilePath, L'\\');
  906. if (pFileNameStart == NULL){
  907. pFileNameStart = pStartPath;
  908. }
  909. // length of file name after the last backslash
  910. UINT fileNameLen = _tcslen(pFileNameStart);
  911. if (fileNameLen > MAXWIDTH-3){
  912. _tcsncpy(outFilePath, pFileNameStart, MAXWIDTH - 3); // 3 spaces for the ellipses
  913. outFilePath[MAXWIDTH - 3] = (TCHAR) NULL;
  914. _tcscat(outFilePath, TEXT("..."));
  915. }
  916. else if (_tcslen(pStartPath) > MAXWIDTH){
  917. _tcsncpy(outFilePath, pStartPath, MAXWIDTH - fileNameLen - 3); // 3 spaces for the ellipses
  918. outFilePath[MAXWIDTH - fileNameLen - 3] = (TCHAR) NULL;
  919. _tcscat(outFilePath, TEXT("..."));
  920. _tcscat(outFilePath, pFileNameStart);
  921. }
  922. else{
  923. _tcscpy(outFilePath, pStartPath);
  924. }
  925. return TRUE;
  926. }
  927. // This strips the volume ID prefix (guid or drive letter)
  928. // and compressed the file name down to 50 characters
  929. // THIS VERSION COMPRESSES IN PLACE
  930. TCHAR * ESICompressFilePath(
  931. IN PTCHAR inFilePath
  932. )
  933. {
  934. const UINT MAXWIDTH = 50;
  935. static TCHAR squishedFileName[MAXWIDTH+1];
  936. PTCHAR pStartPath = NULL;
  937. PTCHAR pFileNameStart = NULL;
  938. // assume that we are prefixed with a GUID
  939. pStartPath = _tcschr(inFilePath, L'}');
  940. if (pStartPath){
  941. pStartPath++; // bump by one to get past the "}"
  942. }
  943. else {
  944. // if that failed, then we will strip off the drive letter
  945. // stuff (e.g. \\.\x:\)
  946. pStartPath = &inFilePath[6];
  947. }
  948. // file name starts at last backslash
  949. pFileNameStart = _tcsrchr(inFilePath, L'\\');
  950. if (pFileNameStart == NULL){
  951. pFileNameStart = pStartPath;
  952. }
  953. // length of file name (after the last backslash)
  954. UINT fileNameLen = _tcslen(pFileNameStart);
  955. // if the file name is longer than MAXWIDTH
  956. if (fileNameLen > MAXWIDTH - 6){
  957. _tcscpy(squishedFileName, TEXT("..."));
  958. _tcsncat(squishedFileName, pFileNameStart, MAXWIDTH - 6); // for the ellipses
  959. squishedFileName[MAXWIDTH - 6] = (TCHAR) NULL;
  960. _tcscat(squishedFileName, TEXT("..."));
  961. }
  962. // if the whole string is longer than MAXWIDTH
  963. else if (_tcslen(pStartPath) > MAXWIDTH - 3){
  964. _tcsncpy(squishedFileName, pStartPath, MAXWIDTH - fileNameLen - 3); // 3 spaces for the ellipses
  965. squishedFileName[MAXWIDTH - fileNameLen - 3] = (TCHAR) NULL;
  966. _tcscat(squishedFileName, TEXT("..."));
  967. _tcscat(squishedFileName, pFileNameStart);
  968. }
  969. // if the whole string will fit
  970. else{
  971. _tcscpy(squishedFileName, pStartPath);
  972. }
  973. return squishedFileName;
  974. }
  975. /*****************************************************************************************************************
  976. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  977. ROUTINE DESCRIPTION:
  978. check if enough free space exists to defrag
  979. INPUT + OUTPUT:
  980. buf is the string for the display message,
  981. bCommandLineMode whether we are in command line mode or not
  982. RETURN:
  983. TRUE - free space exceeds the registry setting
  984. FALSE - not enough free space
  985. */
  986. BOOL ValidateFreeSpace(BOOL bCommandLineMode, LONGLONG llFreeSpace, LONGLONG llUsableFreeSpace,
  987. LONGLONG llDiskSize, TCHAR *VolLabel, TCHAR *returnMsg, UINT returnMsgLen)
  988. {
  989. // get threshold percent from registry
  990. TCHAR cRegValue[100];
  991. HKEY hValue = NULL;
  992. DWORD dwRegValueSize = sizeof(cRegValue);
  993. DWORD dwFreeSpaceErrorLevel = 15;
  994. LONGLONG llFreeSpacePercent = 100 * llFreeSpace / llDiskSize;
  995. LONGLONG llUsableFreeSpacePercent = 100 * llUsableFreeSpace / llDiskSize;
  996. // clear return message string
  997. _tcscpy(returnMsg, TEXT(""));
  998. // get the free space error threshold from the registry
  999. long ret = GetRegValue(
  1000. &hValue,
  1001. TEXT("SOFTWARE\\Microsoft\\Dfrg"),
  1002. TEXT("FreeSpaceErrorLevel"),
  1003. cRegValue,
  1004. &dwRegValueSize);
  1005. RegCloseKey(hValue);
  1006. // convert it and apply range limits
  1007. if (ret == ERROR_SUCCESS)
  1008. {
  1009. dwFreeSpaceErrorLevel = (DWORD) _ttoi(cRegValue);
  1010. // > 50 does not make sense!
  1011. if (dwFreeSpaceErrorLevel > 50)
  1012. dwFreeSpaceErrorLevel = 50;
  1013. // < 0 does not either!
  1014. if (dwFreeSpaceErrorLevel < 1)
  1015. dwFreeSpaceErrorLevel = 0;
  1016. }
  1017. Trace(log, "Validating free space. FreeSpace: %I64d (%I64d%%) UsableFreeSpace: %I64d (%I64d%%) ErrorLevel: %lu%%",
  1018. llFreeSpace, llFreeSpacePercent, llUsableFreeSpace, llUsableFreeSpacePercent, dwFreeSpaceErrorLevel);
  1019. // check usable freespace vs. the threshold
  1020. if (llFreeSpacePercent < dwFreeSpaceErrorLevel)
  1021. {
  1022. TCHAR buf[800];
  1023. TCHAR buf2[800];
  1024. DWORD_PTR dwParams[10];
  1025. // if usable free space is less than reported free space...
  1026. if (llUsableFreeSpacePercent < llFreeSpacePercent)
  1027. {
  1028. //I did it this way because we store UsableFreeSpacePercent as a LONGLONG
  1029. //by Scott K. Sipe
  1030. if (llUsableFreeSpace / llDiskSize > 0 && llUsableFreeSpace / llDiskSize < 1)
  1031. {
  1032. dwParams[0] = (DWORD_PTR) VolLabel;
  1033. dwParams[1] = (DWORD_PTR) llFreeSpacePercent;
  1034. dwParams[2] = dwFreeSpaceErrorLevel;
  1035. dwParams[3] = NULL;
  1036. LoadString(GetDfrgResHandle(), IDS_NO_USABLE_FREE_SPACE_LESS_1, buf, sizeof(buf) / sizeof(TCHAR));
  1037. if (!bCommandLineMode)
  1038. {
  1039. LoadString(GetDfrgResHandle(), IDS_DEFRAG_NOW_ANYWAY, buf2, sizeof(buf2) / sizeof(TCHAR));
  1040. _tcscat(buf, buf2);
  1041. }
  1042. int len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1043. buf, 0, 0, returnMsg, returnMsgLen, (va_list*) dwParams);
  1044. }
  1045. else
  1046. {
  1047. dwParams[0] = (DWORD_PTR) VolLabel;
  1048. dwParams[1] = (DWORD_PTR) llFreeSpacePercent;
  1049. dwParams[2] = (DWORD_PTR) llUsableFreeSpacePercent;
  1050. dwParams[3] = dwFreeSpaceErrorLevel;
  1051. LoadString(GetDfrgResHandle(), IDS_NO_USABLE_FREE_SPACE, buf, sizeof(buf) / sizeof(TCHAR));
  1052. if (!bCommandLineMode)
  1053. {
  1054. LoadString(GetDfrgResHandle(), IDS_DEFRAG_NOW_ANYWAY, buf2, sizeof(buf2) / sizeof(TCHAR));
  1055. _tcscat(buf, buf2);
  1056. }
  1057. int len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1058. buf, 0, 0, returnMsg, returnMsgLen, (va_list*) dwParams);
  1059. }
  1060. }
  1061. // if usable freespace is not less than reported free space...
  1062. else
  1063. {
  1064. if (llUsableFreeSpace / llDiskSize > 0 && llUsableFreeSpace / llDiskSize < 1)
  1065. {
  1066. dwParams[0] = (DWORD_PTR) VolLabel;
  1067. dwParams[1] = dwFreeSpaceErrorLevel;
  1068. dwParams[2] = NULL;
  1069. dwParams[3] = NULL;
  1070. LoadString(GetDfrgResHandle(), IDS_NO_FREE_SPACE_LESS_1, buf, sizeof(buf) / sizeof(TCHAR));
  1071. if (!bCommandLineMode)
  1072. {
  1073. LoadString(GetDfrgResHandle(), IDS_DEFRAG_NOW_ANYWAY, buf2, sizeof(buf2) / sizeof(TCHAR));
  1074. _tcscat(buf, buf2);
  1075. }
  1076. int len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1077. buf, 0, 0, returnMsg, returnMsgLen, (va_list*) dwParams);
  1078. }
  1079. else
  1080. {
  1081. dwParams[0] = (DWORD_PTR) VolLabel;
  1082. dwParams[1] = (DWORD_PTR) llUsableFreeSpacePercent;
  1083. dwParams[2] = dwFreeSpaceErrorLevel;
  1084. dwParams[3] = NULL;
  1085. LoadString(GetDfrgResHandle(), IDS_NO_FREE_SPACE, buf, sizeof(buf) / sizeof(TCHAR));
  1086. if (!bCommandLineMode)
  1087. {
  1088. LoadString(GetDfrgResHandle(), IDS_DEFRAG_NOW_ANYWAY, buf2, sizeof(buf2) / sizeof(TCHAR));
  1089. _tcscat(buf, buf2);
  1090. }
  1091. int len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1092. buf, 0, 0, returnMsg, returnMsgLen, (va_list*) dwParams);
  1093. }
  1094. }
  1095. return FALSE;
  1096. }
  1097. return TRUE;
  1098. }
  1099. /*****************************************************************************************************************
  1100. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1101. ROUTINE DESCRIPTION:
  1102. Finds out if this is the boot volume.
  1103. INPUT:
  1104. TCHAR tDrive The drive letter we are defragmenting
  1105. RETURN:
  1106. Returns TRUE if this is boot value, else FALSE if it is not.
  1107. */
  1108. BOOL IsBootVolume(
  1109. IN TCHAR tDrive
  1110. )
  1111. {
  1112. TCHAR tExpandedSystemRoot[MAX_PATH + 2]; //the expanded system root string
  1113. TCHAR tExpandedSystemRootDrive;
  1114. //I get the environment variable and expand it to get what the current boot
  1115. //drive is. The first character will be the system boot drive. compare this
  1116. //with the drive that is selected, and test is we are boot optimizing the
  1117. //boot drive
  1118. if (GetSystemWindowsDirectory(tExpandedSystemRoot, MAX_PATH + 1) == 0)
  1119. {
  1120. return FALSE;
  1121. }
  1122. tExpandedSystemRoot[MAX_PATH + 1] = TEXT('\0');
  1123. //convert the characters to upper case before comparison
  1124. tExpandedSystemRootDrive = towupper(tExpandedSystemRoot[0]);
  1125. //test to see if the drive letters match
  1126. if(tDrive == tExpandedSystemRootDrive) //first two characters are equal
  1127. {
  1128. return TRUE;
  1129. } else
  1130. {
  1131. return FALSE;
  1132. }
  1133. return TRUE;
  1134. }