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.

2244 lines
72 KiB

  1. /**************************************************************************************************
  2. FILENAME: BootOptimize.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. DESCRIPTION:
  5. Boot Optimize 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<nt.h>
  14. #include<ntrtl.h>
  15. #include<nturtl.h>
  16. #include "Windows.h"
  17. #include <winioctl.h>
  18. #include <math.h>
  19. #include <fcntl.h>
  20. extern "C" {
  21. #include "SysStruc.h"
  22. }
  23. #include "BootOptimizeNtfs.h"
  24. #include "DfrgCmn.h"
  25. #include "GetReg.h"
  26. #include "defragcommon.h"
  27. #include "Devio.h"
  28. #include "movefile.h"
  29. #include "fssubs.h"
  30. #include "Alloc.h"
  31. #define THIS_MODULE 'B'
  32. #include "logfile.h"
  33. #include "ntfssubs.h"
  34. #include "dfrgengn.h"
  35. #include "FreeSpace.h"
  36. #include "extents.h"
  37. #include "dfrgntfs.h"
  38. //
  39. // Hard-coded registry keys that we access to find the path to layout.ini,
  40. // and other persisted data of interest (such as the boot optimise exclude
  41. // zone beginning and end markers).
  42. //
  43. #define OPTIMAL_LAYOUT_KEY_PATH TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OptimalLayout")
  44. #define OPTIMAL_LAYOUT_FILE_VALUE_NAME TEXT("LayoutFilePath")
  45. #define BOOT_OPTIMIZE_REGISTRY_PATH TEXT("SOFTWARE\\Microsoft\\Dfrg\\BootOptimizeFunction")
  46. #define BOOT_OPTIMIZE_ENABLE_FLAG TEXT("Enable")
  47. #define BOOT_OPTIMIZE_REGISTRY_LCNSTARTLOCATION TEXT("LcnStartLocation")
  48. #define BOOT_OPTIMIZE_REGISTRY_LCNENDLOCATION TEXT("LcnEndLocation")
  49. #define BOOT_OPTIMIZE_REGISTRY_COMPLETE TEXT("OptimizeComplete")
  50. #define BOOT_OPTIMIZE_REGISTRY_ERROR TEXT("OptimizeError")
  51. #define BOOT_OPTIMIZE_LAST_WRITTEN_DATETIME TEXT("FileTimeStamp")
  52. #define BOOT_OPTIMIZE_MAX_FILE_SIZE_BYTES (32 * 1024 * 1024)
  53. #define BOOT_OPTIMIZE_MAX_ZONE_SIZE_MB ((LONGLONG) (4 * 1024))
  54. #define BOOT_OPTIMIZE_MAX_ZONE_SIZE_PERCENT (50)
  55. #define BOOT_OPTIMIZE_ZONE_EXTEND_PERCENT (150)
  56. #define BOOT_OPTIMISE_ZONE_RELOCATE_THRESHOLD (90)
  57. #define BOOT_OPTIMIZE_ZONE_EXTEND_MIN_SIZE_BYTES (100 * 1024 * 1024)
  58. BOOL
  59. UpdateInMultipleTrees(
  60. IN PFREE_SPACE_ENTRY pOldEntry,
  61. IN PFREE_SPACE_ENTRY pNewEntry
  62. );
  63. /*****************************************************************************************************************
  64. ROUTINE DESCRIPTION:
  65. Get a rough idea of how many records are in the file and triple it, to make an estimation
  66. of how many files are in the boot optimize file, and I triple it to account for multiple
  67. stream files. Also make the assumption that the file count is atleast 300, so that I can
  68. allocate enough memory to hold all the records.
  69. INPUT:
  70. full path name to the boot optimize file
  71. RETURN:
  72. triple the number of records in the boot optimize file.
  73. */
  74. DWORD CountNumberofRecordsinFile(
  75. IN LPCTSTR lpBootOptimzePath
  76. )
  77. {
  78. DWORD dwNumberofRecords = 0; //the number of records in the input file
  79. TCHAR tBuffer [MAX_PATH]; //temporary buffer to the input string
  80. ULONG ulLength; //length of the line read in by fgetts
  81. FILE* fBootOptimizeFile; //File Pointer to fBootOptimizeFile
  82. //set read mode to binary
  83. _fmode = _O_BINARY;
  84. //open the file
  85. //if I can't open the file, return a record count of zero
  86. fBootOptimizeFile = _tfopen(lpBootOptimzePath,TEXT("r"));
  87. if(fBootOptimizeFile == NULL)
  88. {
  89. return 0;
  90. }
  91. //read the entire file and count the number of records
  92. while(_fgetts(tBuffer,MAX_PATH - 1,fBootOptimizeFile) != 0)
  93. {
  94. // check for terminating carriage return.
  95. ulLength = wcslen(tBuffer);
  96. if (ulLength && (tBuffer[ulLength - 1] == TEXT('\n'))) {
  97. dwNumberofRecords++;
  98. }
  99. }
  100. fclose(fBootOptimizeFile);
  101. //triple the number of records we have
  102. if(dwNumberofRecords < 100)
  103. {
  104. dwNumberofRecords = 100;
  105. }
  106. return dwNumberofRecords;
  107. }
  108. /******************************************************************************
  109. ROUTINE DESCRIPTION:
  110. This allocates memory of size cbSize bytes. Note that cbSize MUST be the
  111. size we're expecting it to be (based on the slab-allocator initialisation),
  112. since our slab allocator can only handle packets of one size.
  113. INPUT:
  114. pTable - The table that the comparison is being made for (not used)
  115. cbSize - The count in bytes of the memory needed
  116. RETURN:
  117. Pointer to allocated memory of size cbSize; NULL if the system is out
  118. of memory, or cbSize is not what the slab allocator was initialised with.
  119. */
  120. PVOID
  121. NTAPI
  122. BootOptimiseAllocateRoutine(
  123. IN PRTL_GENERIC_TABLE pTable,
  124. IN CLONG cbSize
  125. )
  126. {
  127. PVOID pMemory = NULL;
  128. //
  129. // Sanity-check to make sure that we're being asked for packets of the
  130. // "correct" size, since our slab-allocator can only deal with packets
  131. // of a given size
  132. //
  133. if ((cbSize + sizeof(PVOID)) == VolData.SaBootOptimiseFilesContext.dwPacketSize) {
  134. //
  135. // size was correct; call our allocator
  136. //
  137. pMemory = SaAllocatePacket(&VolData.SaBootOptimiseFilesContext);
  138. }
  139. else {
  140. //
  141. // Oops, we have a problem!
  142. //
  143. Trace(error, "Internal Error. BootOptimiseAllocateRoutine called with "
  144. "unexpected size (%lu instead of %lu).",
  145. cbSize, VolData.SaBootOptimiseFilesContext.dwPacketSize - sizeof(PVOID));
  146. assert(FALSE);
  147. }
  148. return pMemory;
  149. UNREFERENCED_PARAMETER(pTable);
  150. }
  151. /******************************************************************************
  152. ROUTINE DESCRIPTION:
  153. This frees a packet allocated by BootOptimiseAllocateRoutine
  154. INPUT:
  155. pTable - The table that the comparison is being made for (not used)
  156. pvBuffer - Pointer to the memory to be freed. This pointer should not
  157. be used after this routine is called.
  158. RETURN:
  159. VOID
  160. */
  161. VOID
  162. NTAPI
  163. BootOptimiseFreeRoutine(
  164. IN PRTL_GENERIC_TABLE pTable,
  165. IN PVOID pvBuffer
  166. )
  167. {
  168. assert(pvBuffer);
  169. SaFreePacket(&VolData.SaBootOptimiseFilesContext, pvBuffer);
  170. UNREFERENCED_PARAMETER(pTable);
  171. }
  172. /******************************************************************************
  173. ROUTINE DESCRIPTION:
  174. Comparison routine to compare the FileRecordNumber of two FILE_LIST_ENTRY
  175. records.
  176. INPUT:
  177. pTable - the table that the comparison is being made for (not used)
  178. pNode1 - the first FILE_LIST_ENTRY to be compared
  179. pNode2 - the second FILE_LIST_ENTRY to be compared
  180. RETURN:
  181. RtlGenericLessThan if pNode1 < pNode2
  182. RtlGenericGreaterThan if pNode1 > pNode2
  183. RtlGenericEqual if pNode1 == pNode2
  184. */
  185. RTL_GENERIC_COMPARE_RESULTS
  186. NTAPI
  187. BootOptimiseFrnCompareRoutine(
  188. IN PRTL_GENERIC_TABLE pTable,
  189. IN PVOID pNode1,
  190. IN PVOID pNode2
  191. )
  192. {
  193. PFILE_LIST_ENTRY pEntry1 = (PFILE_LIST_ENTRY) pNode1;
  194. PFILE_LIST_ENTRY pEntry2 = (PFILE_LIST_ENTRY) pNode2;
  195. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  196. //
  197. // These shouldn't ever be NULL
  198. //
  199. assert(pNode1 && pNode2);
  200. if (pEntry1->FileRecordNumber < pEntry2->FileRecordNumber) {
  201. result = GenericLessThan;
  202. }
  203. else if (pEntry1->FileRecordNumber > pEntry2->FileRecordNumber) {
  204. result = GenericGreaterThan;
  205. }
  206. //
  207. // Default is GenericEqual
  208. //
  209. return result;
  210. }
  211. /******************************************************************************
  212. ROUTINE DESCRIPTION:
  213. Comparison routine to compare the StartingLcn of two FILE_LIST_ENTRY
  214. records.
  215. INPUT:
  216. pTable - the table that the comparison is being made for (not used)
  217. pNode1 - the first FILE_LIST_ENTRY to be compared
  218. pNode2 - the second FILE_LIST_ENTRY to be compared
  219. RETURN:
  220. RtlGenericLessThan if pNode1 < pNode2
  221. RtlGenericGreaterThan if pNode1 > pNode2
  222. RtlGenericEqual if pNode1 == pNode2
  223. */
  224. RTL_GENERIC_COMPARE_RESULTS
  225. NTAPI
  226. BootOptimiseStartLcnCompareRoutine(
  227. IN PRTL_GENERIC_TABLE pTable,
  228. PVOID pNode1,
  229. PVOID pNode2
  230. )
  231. {
  232. PFILE_LIST_ENTRY pEntry1 = (PFILE_LIST_ENTRY) pNode1;
  233. PFILE_LIST_ENTRY pEntry2 = (PFILE_LIST_ENTRY) pNode2;
  234. RTL_GENERIC_COMPARE_RESULTS result = GenericEqual;
  235. //
  236. // These shouldn't ever be NULL
  237. //
  238. assert(pNode1 && pNode2);
  239. if (pEntry1->StartingLcn < pEntry2->StartingLcn) {
  240. result = GenericLessThan;
  241. }
  242. else if (pEntry1->StartingLcn > pEntry2->StartingLcn) {
  243. result = GenericGreaterThan;
  244. }
  245. //
  246. // Default is GenericEqual
  247. //
  248. return result;
  249. }
  250. /******************************************************************************
  251. ROUTINE DESCRIPTION:
  252. Initialisation routine for the BootOptimiseTables.
  253. INPUT:
  254. pBootOptimiseTable - pointer to table that will contain a list of
  255. files that are to be preferentially laid out at the start of the disk
  256. pFilesInExcludeZoneTable - pointer to the table that will contain a list
  257. of all the files that are in the boot-optimise zone but not in the
  258. boot-optimise table (i.e, this table containts the list of files that
  259. need to be evicted)
  260. RETURN:
  261. TRUE - Initialisation completed successfully
  262. FALSE - Fatal errors were encountered during initialisation
  263. */
  264. BOOL
  265. InitialiseBootOptimiseTables(
  266. IN PRTL_GENERIC_TABLE pBootOptimiseTable,
  267. IN PRTL_GENERIC_TABLE pFilesInExcludeZoneTable
  268. )
  269. {
  270. PVOID pTableContext = NULL;
  271. BOOL bResult = FALSE;
  272. //
  273. // Initialise the Slab Allocator context that will be used to allocate
  274. // packets for these two tables. The two tables will be holding
  275. // FILE_LIST_ENTRYs.
  276. //
  277. bResult = SaInitialiseContext(&VolData.SaBootOptimiseFilesContext,
  278. sizeof(FILE_LIST_ENTRY),
  279. 64*1024);
  280. //
  281. // And initialise the two tables
  282. //
  283. if (bResult) {
  284. RtlInitializeGenericTable(pBootOptimiseTable,
  285. BootOptimiseFrnCompareRoutine,
  286. BootOptimiseAllocateRoutine,
  287. BootOptimiseFreeRoutine,
  288. pTableContext);
  289. RtlInitializeGenericTable(pFilesInExcludeZoneTable,
  290. BootOptimiseStartLcnCompareRoutine,
  291. BootOptimiseAllocateRoutine,
  292. BootOptimiseFreeRoutine,
  293. pTableContext);
  294. }
  295. return bResult;
  296. }
  297. /******************************************************************************
  298. ROUTINE DESCRIPTION:
  299. Routine to free all the packets belonging to the two tables, and re-init
  300. them.
  301. INPUT:
  302. pBootOptimiseTable - pointer to table that contains a list of files that
  303. are to be preferentially laid out at the beginning of the disk
  304. pFilesInExcludeZoneTable - pointer to the table that contains a list
  305. of all the files that are in the boot-optimise zone but not in the
  306. boot-optimise table (i.e, files that need to be evicted)
  307. RETURN:
  308. VOID
  309. */
  310. VOID
  311. UnInitialiseBootOptimiseTables(
  312. IN PRTL_GENERIC_TABLE pBootOptimiseTable,
  313. IN PRTL_GENERIC_TABLE pFilesInExcludeZoneTable
  314. )
  315. {
  316. PVOID pTableContext = NULL;
  317. BOOL bResult = FALSE;
  318. RtlInitializeGenericTable(pBootOptimiseTable,
  319. BootOptimiseFrnCompareRoutine,
  320. BootOptimiseAllocateRoutine,
  321. BootOptimiseFreeRoutine,
  322. pTableContext);
  323. RtlInitializeGenericTable(pFilesInExcludeZoneTable,
  324. BootOptimiseStartLcnCompareRoutine,
  325. BootOptimiseAllocateRoutine,
  326. BootOptimiseFreeRoutine,
  327. pTableContext);
  328. SaFreeAllPackets(&VolData.SaBootOptimiseFilesContext);
  329. }
  330. /******************************************************************************
  331. ROUTINE DESCRIPTION:
  332. Open the specified file with read and synchronize attributes, and return
  333. a handle to it.
  334. INPUT:
  335. lpFilePath - file to be opened
  336. RETURN:
  337. HANDLE to the file or INVALID_HANDLE_VALUE
  338. */
  339. HANDLE
  340. GetFileHandle(
  341. IN LPCTSTR lpFilePath
  342. )
  343. {
  344. HANDLE hFile = INVALID_HANDLE_VALUE;
  345. hFile = CreateFile(lpFilePath,
  346. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  347. 0,
  348. NULL,
  349. OPEN_EXISTING,
  350. FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT,
  351. NULL
  352. );
  353. return hFile;
  354. }
  355. /******************************************************************************
  356. ROUTINE DESCRIPTION:
  357. Get the FileRecordNumber for a file given a handle to it
  358. INPUT:
  359. hFile - handle to the file of interest
  360. RETURN:
  361. FRN for the given file, -1 if errors were encountered.
  362. */
  363. LONGLONG
  364. GetFileRecordNumber(
  365. IN CONST HANDLE hFile
  366. )
  367. {
  368. FILE_INTERNAL_INFORMATION internalInformation;
  369. IO_STATUS_BLOCK ioStatusBlock;
  370. LONGLONG fileRecordNumber = -1;
  371. NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
  372. ZeroMemory(&internalInformation, sizeof(FILE_INTERNAL_INFORMATION));
  373. ZeroMemory(&ioStatusBlock, sizeof(IO_STATUS_BLOCK));
  374. //
  375. // The FileRecordNumber is the lower part of the InternalInformation
  376. // returned for the file
  377. //
  378. ntStatus = NtQueryInformationFile(hFile,
  379. &ioStatusBlock,
  380. &internalInformation,
  381. sizeof(FILE_INTERNAL_INFORMATION),
  382. FileInternalInformation
  383. );
  384. if (NT_SUCCESS(ntStatus) && (NT_SUCCESS(ioStatusBlock.Status))) {
  385. //
  386. // The FRN is the lower 48-bits of the value returned
  387. //
  388. fileRecordNumber = (LONGLONG) (internalInformation.IndexNumber.QuadPart & 0x0000FFFFFFFFFFFF);
  389. }
  390. return fileRecordNumber;
  391. }
  392. /******************************************************************************
  393. ROUTINE DESCRIPTION:
  394. Get the size of the file in clusters from calling FSCL_GET_RETRIEVAL_POINTERS.
  395. INPUT:
  396. hFile - The handle to the file of interest
  397. RETURN:
  398. The size of the file in clusters
  399. */
  400. LONGLONG
  401. GetFileSizeInfo(
  402. IN HANDLE hFile
  403. )
  404. {
  405. ULONGLONG ulSizeofFileInClusters = 0; //size of the file in clusters
  406. int i;
  407. ULONGLONG startVcn = 0; //starting VCN of the file, always 0
  408. STARTING_VCN_INPUT_BUFFER startingVcn; //starting VCN Buffer
  409. ULONG BytesReturned = 0; //number of bytes returned by ESDeviceIoControl
  410. HANDLE hRetrievalPointersBuffer = NULL; //Handle to the Retrieval Pointers Buffer
  411. PRETRIEVAL_POINTERS_BUFFER pRetrievalPointersBuffer = NULL; //pointer to the Retrieval Pointer
  412. PLARGE_INTEGER pRetrievalPointers = NULL; //Pointer to retrieval pointers
  413. ULONG RetrievalPointers = 0x100; //Number of extents for the file, try 256 first
  414. BOOL bGetRetrievalPointersMore = TRUE; //boolean to test the end of getting retrieval pointers
  415. if (INVALID_HANDLE_VALUE == hFile) {
  416. return 0;
  417. }
  418. // zero the memory of the starting VCN input buffer
  419. ZeroMemory(&startVcn, sizeof(STARTING_VCN_INPUT_BUFFER));
  420. // Read the retrieval pointers into a buffer in memory.
  421. while (bGetRetrievalPointersMore) {
  422. //0.0E00 Allocate a RetrievalPointersBuffer.
  423. if (!AllocateMemory(sizeof(RETRIEVAL_POINTERS_BUFFER) + (RetrievalPointers * 2 * sizeof(LARGE_INTEGER)),
  424. &hRetrievalPointersBuffer,
  425. (void**)(PCHAR*)&pRetrievalPointersBuffer)) {
  426. return 0;
  427. }
  428. startingVcn.StartingVcn.QuadPart = 0;
  429. if(ESDeviceIoControl(hFile,
  430. FSCTL_GET_RETRIEVAL_POINTERS,
  431. &startingVcn,
  432. sizeof(STARTING_VCN_INPUT_BUFFER),
  433. pRetrievalPointersBuffer,
  434. (DWORD)GlobalSize(hRetrievalPointersBuffer),
  435. &BytesReturned,
  436. NULL)) {
  437. bGetRetrievalPointersMore = FALSE;
  438. }
  439. else {
  440. //This occurs on a zero length file (no clusters allocated).
  441. if(GetLastError() == ERROR_HANDLE_EOF) {
  442. //file is zero lenght, so return 0
  443. //free the memory for the retrival pointers
  444. //the while loop makes sure all occurances are unlocked
  445. while (GlobalUnlock(hRetrievalPointersBuffer))
  446. {
  447. ;
  448. }
  449. GlobalFree(hRetrievalPointersBuffer);
  450. hRetrievalPointersBuffer = NULL;
  451. return 0;
  452. }
  453. //0.0E00 Check to see if the error is not because the buffer is too small.
  454. if(GetLastError() == ERROR_MORE_DATA)
  455. {
  456. //0.1E00 Double the buffer size until it's large enough to hold the file's extent list.
  457. RetrievalPointers *= 2;
  458. } else
  459. {
  460. //some other error, return 0
  461. //free the memory for the retrival pointers
  462. //the while loop makes sure all occurances are unlocked
  463. while (GlobalUnlock(hRetrievalPointersBuffer))
  464. {
  465. ;
  466. }
  467. GlobalFree(hRetrievalPointersBuffer);
  468. hRetrievalPointersBuffer = NULL;
  469. return 0;
  470. }
  471. }
  472. }
  473. //loop through the retrival pointer list and add up the size of the file
  474. startVcn = pRetrievalPointersBuffer->StartingVcn.QuadPart;
  475. for (i = 0; i < (ULONGLONG) pRetrievalPointersBuffer->ExtentCount; i++)
  476. {
  477. ulSizeofFileInClusters += pRetrievalPointersBuffer->Extents[i].NextVcn.QuadPart - startVcn;
  478. startVcn = pRetrievalPointersBuffer->Extents[i].NextVcn.QuadPart;
  479. }
  480. if(hRetrievalPointersBuffer != NULL)
  481. {
  482. //free the memory for the retrival pointers
  483. //the while loop makes sure all occurances are unlocked
  484. while (GlobalUnlock(hRetrievalPointersBuffer))
  485. {
  486. ;
  487. }
  488. GlobalFree(hRetrievalPointersBuffer);
  489. hRetrievalPointersBuffer = NULL;
  490. }
  491. return ulSizeofFileInClusters;
  492. }
  493. /******************************************************************************
  494. ROUTINE DESCRIPTION:
  495. Checks if we have a valid file to be laid out at the beginning of the disk.
  496. INPUT:
  497. lpFilePath - The file name input from the list--typically, a line from
  498. layout.ini
  499. tcBootVolumeDriveLetter - Drive letter of the boot volume
  500. bIsNtfs - TRUE if the volume is NTFS, FALSE otherwise
  501. OUTPUT:
  502. pFileRecordNumber - The FRN of the file, if it is a valid file
  503. pClusterCount - The file-size (in clusters), if it is a valid file
  504. RETURN:
  505. TRUE if this is a valid file,
  506. FALSE if it is not.
  507. */
  508. BOOL IsAValidFile(
  509. IN LPTSTR lpFilePath,
  510. IN CONST TCHAR tcBootVolumeDriveLetter,
  511. IN CONST BOOL bIsNtfs,
  512. OUT LONGLONG *pFileRecordNumber OPTIONAL,
  513. OUT LONGLONG *pClusterCount OPTIONAL
  514. )
  515. {
  516. TCHAR tcFileName[MAX_PATH+1]; // Just the file name portion of lpFilePath
  517. TCHAR tcFileDriveLetter; // Drive letter for current file (lpFilePath)
  518. HANDLE hFile = NULL; // Temporary handle to check file size, etc
  519. BOOL bFileIsDirectory = FALSE; // Flag to check if current file is a dir
  520. LONGLONG FileSizeClusters = 0;
  521. BY_HANDLE_FILE_INFORMATION FileInformation; // For checking if this is a directory
  522. // Ignore blank lines, and the root directory, in layout.ini
  523. if (!lpFilePath || _tcslen(lpFilePath) <= 2) {
  524. return FALSE;
  525. }
  526. // Ignore the group headers
  527. if (NULL != _tcsstr(lpFilePath, TEXT("[OptimalLayoutFile]"))) {
  528. return FALSE;
  529. }
  530. // Ignore the file = and version = lines
  531. if(NULL != _tcsstr(lpFilePath, TEXT("Version="))) {
  532. return FALSE;
  533. }
  534. //get the drive the file is on, if its not the boot drive, skip the file
  535. tcFileDriveLetter = towupper(lpFilePath[0]);
  536. if(tcFileDriveLetter != tcBootVolumeDriveLetter) { //files are on boot drive else skip them
  537. return FALSE;
  538. }
  539. if ((lpFilePath[1] != TEXT(':')) ||
  540. (lpFilePath[2] != TEXT('\\'))) {
  541. return FALSE;
  542. }
  543. //get just the file name from the end of the path
  544. if(_tcsrchr(lpFilePath,TEXT('\\')) != NULL) {
  545. _tcscpy(tcFileName,_tcsrchr(lpFilePath,TEXT('\\'))+1);
  546. }
  547. else {
  548. //not a valid name
  549. return FALSE;
  550. }
  551. if(_tcsicmp(tcFileName,TEXT("BOOTSECT.DOS")) == 0) {
  552. return FALSE;
  553. }
  554. if(_tcsicmp(tcFileName,TEXT("SAFEBOOT.FS")) == 0) {
  555. return FALSE;
  556. }
  557. if(_tcsicmp(tcFileName,TEXT("SAFEBOOT.CSV")) == 0) {
  558. return FALSE;
  559. }
  560. if(_tcsicmp(tcFileName,TEXT("SAFEBOOT.RSV")) == 0) {
  561. return FALSE;
  562. }
  563. if(_tcsicmp(tcFileName,TEXT("HIBERFIL.SYS")) == 0) {
  564. return FALSE;
  565. }
  566. if(_tcsicmp(tcFileName,TEXT("MEMORY.DMP")) == 0) {
  567. return FALSE;
  568. }
  569. if(_tcsicmp(tcFileName,TEXT("PAGEFILE.SYS")) == 0) {
  570. return FALSE;
  571. }
  572. // so far, so good. Now, we need to check if the file exists, and is
  573. // too big
  574. hFile = GetFileHandle(lpFilePath);
  575. if (INVALID_HANDLE_VALUE == hFile) {
  576. return FALSE;
  577. }
  578. // determine if directory file.
  579. bFileIsDirectory = FALSE;
  580. if (GetFileInformationByHandle(hFile, &FileInformation)) {
  581. bFileIsDirectory = (FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  582. }
  583. if ((bFileIsDirectory) && (!bIsNtfs)) {
  584. CloseHandle(hFile);
  585. return FALSE;
  586. }
  587. if (pFileRecordNumber) {
  588. *pFileRecordNumber = GetFileRecordNumber(hFile);
  589. }
  590. FileSizeClusters = GetFileSizeInfo(hFile);
  591. if (pClusterCount) {
  592. *pClusterCount = FileSizeClusters;
  593. }
  594. CloseHandle(hFile);
  595. // We won't move files that are bigger than BOOT_OPTIMIZE_MAX_FILE_SIZE_BYTES MB
  596. if (FileSizeClusters > (BOOT_OPTIMIZE_MAX_FILE_SIZE_BYTES / VolData.BytesPerCluster)) {
  597. return FALSE;
  598. }
  599. //file is OK, return TRUE
  600. return TRUE;
  601. }
  602. /******************************************************************************
  603. ROUTINE DESCRIPTION:
  604. Reads the layout.ini at the given location, and builds a list of valid
  605. files that should be preferentially laid out at the start of the disk.
  606. INPUT:
  607. lpLayoutIni - Fill path to the file (layout.ini) containing the
  608. list of files to be preferentially laid out.
  609. tcBootVolumeDriveLetter - Drive letter of the boot volume
  610. OUTPUT:
  611. pBootOptimiseTable - Table to contain the files of interest
  612. pClustersNeeded - The size (in clusters), that is needed for all the files
  613. in the list.
  614. RETURN:
  615. TRUE if the list could successfully be built
  616. FALSE otherwise
  617. */
  618. BOOL
  619. BuildBootOptimiseFileList(
  620. IN OUT PRTL_GENERIC_TABLE pBootOptimiseTable,
  621. IN LPCTSTR lpLayoutIni,
  622. IN CONST TCHAR tcBootVolumeDriveLetter,
  623. IN CONST BOOL bIsNtfs,
  624. OUT LONGLONG *pClustersNeeded
  625. )
  626. {
  627. PVOID pTableContext = NULL; // Temporary value used for the AVL Tables
  628. BOOL bResult = TRUE; // The value to be returned
  629. TCHAR tBuffer [MAX_PATH+1]; // Temporary buffer to the input string
  630. ULONG ulLength = 0; // Length of the line read in by fgetts
  631. FILE* fpLayoutIni = NULL; // File pointer to layout.ini
  632. LONGLONG llClusterCount = 0, // ClusterCount of current File
  633. llFileRecordNumber = -1; // FRN of current file
  634. PVOID pvTemp = NULL; // Temporary value used for AVL Tables
  635. BOOLEAN bNewElement = FALSE; // Temporary value used for AVL Tables
  636. FILE_LIST_ENTRY FileEntry; // Current File
  637. DWORD dwNumberofRecords = 0,
  638. dwIndex = 0;
  639. // Initialise out parameters
  640. *pClustersNeeded = 0;
  641. // Zero out local structs
  642. ZeroMemory(&FileEntry, sizeof(FILE_LIST_ENTRY));
  643. //
  644. // Get a count of the number of entries in layout.ini, so that we can
  645. // allocate an array to keep track of the LayoutIniEntryIndex <-> FRN
  646. // mapping
  647. //
  648. dwNumberofRecords = 10 + CountNumberofRecordsinFile(lpLayoutIni);
  649. if (dwNumberofRecords <= 10) {
  650. bResult = FALSE;
  651. goto EXIT;
  652. }
  653. Trace(log, "Number of Layout.Ini entries: %d", dwNumberofRecords-10);
  654. if (!AllocateMemory(
  655. (DWORD) (sizeof(LONGLONG) * dwNumberofRecords),
  656. &(VolData.hBootOptimiseFrnList),
  657. (PVOID*) &(VolData.pBootOptimiseFrnList)
  658. )) {
  659. bResult = FALSE;
  660. goto EXIT;
  661. }
  662. // Set read mode to binary: layout.ini is a UNICODE file
  663. _fmode = _O_BINARY;
  664. // Open the file
  665. fpLayoutIni = _tfopen(lpLayoutIni,TEXT("r"));
  666. if (fpLayoutIni) {
  667. // Read the entire file and check each file to make sure its valid,
  668. // and then add to the list
  669. while (_fgetts(tBuffer,MAX_PATH,fpLayoutIni) != 0) {
  670. // Remove terminating carriage return.
  671. ulLength = wcslen(tBuffer);
  672. if (ulLength < 3) {
  673. continue;
  674. }
  675. if (tBuffer[ulLength - 1] == TEXT('\n')) {
  676. tBuffer[ulLength - 1] = 0;
  677. ulLength--;
  678. if (tBuffer[ulLength - 1] == TEXT('\r')) {
  679. tBuffer[ulLength - 1] = 0;
  680. ulLength--;
  681. }
  682. } else {
  683. continue;
  684. }
  685. if (IsAValidFile(
  686. tBuffer,
  687. tcBootVolumeDriveLetter,
  688. bIsNtfs,
  689. &llFileRecordNumber,
  690. &llClusterCount)
  691. ) {
  692. // This is a valid file, copy the information of interest to
  693. // the FILE_LIST_ENTRY structure and add it to our list.
  694. //
  695. // We set the starting LCN to max value at first (since we
  696. // don't have this information at this time)--this will be
  697. // set to the correct value during the analysis phase.
  698. //
  699. FileEntry.StartingLcn = VolData.TotalClusters;
  700. FileEntry.ClusterCount = llClusterCount;
  701. FileEntry.FileRecordNumber = llFileRecordNumber;
  702. // Keep track of the total clusters needed
  703. (*pClustersNeeded) += llClusterCount;
  704. // And add this entry to our tree
  705. pvTemp = RtlInsertElementGenericTable(
  706. pBootOptimiseTable,
  707. (PVOID) &FileEntry,
  708. sizeof(FILE_LIST_ENTRY),
  709. &bNewElement);
  710. if (!pvTemp) {
  711. // An allocation failed
  712. bResult = FALSE;
  713. assert(FALSE);
  714. break;
  715. }
  716. if (dwIndex < dwNumberofRecords) {
  717. VolData.pBootOptimiseFrnList[dwIndex] = llFileRecordNumber;
  718. ++dwIndex;
  719. }
  720. }
  721. }
  722. //
  723. // Make sure we have an FRN of -1, at the end of the list, even if it
  724. // means wiping the last real FRN (which should never be the case)
  725. //
  726. if (dwIndex >= dwNumberofRecords) {
  727. dwIndex = dwNumberofRecords - 1;
  728. }
  729. VolData.pBootOptimiseFrnList[dwIndex] = -1;
  730. //close the file at the end
  731. fclose(fpLayoutIni);
  732. }
  733. else {
  734. // Layout.Ini could not be opened for read access
  735. bResult = FALSE;
  736. }
  737. EXIT:
  738. return bResult;
  739. }
  740. /******************************************************************************
  741. ROUTINE DESCRIPTION:
  742. If the current file is on the list of files to be preferentially laid out
  743. at the beginning of the disk, this routine updates the file record in our
  744. AVL-tree with fields from VolData.
  745. INPUT:
  746. (Global) Various VolData fields
  747. OUTPUT:
  748. None;
  749. May change an entry in VolData.BootOptimiseFileTable
  750. RETURN:
  751. TRUE if the file exists in our preferred list, and was updated
  752. FALSE if the file is not one we're interesed in preferentially laying out
  753. */
  754. BOOL
  755. UpdateInBootOptimiseList(
  756. IN PFILE_LIST_ENTRY pFileListEntry
  757. )
  758. {
  759. FILE_LIST_ENTRY FileEntryToSearchFor;
  760. PFILE_LIST_ENTRY pClosestMatchEntry = NULL;
  761. PFILE_EXTENT_HEADER pFileExtentHeader = NULL;
  762. static ULONG ulDeleteCount = 0;
  763. PVOID pRestartKey = NULL;
  764. LONGLONG FileRecordNumberToSearchFor = 0;
  765. ZeroMemory(&FileEntryToSearchFor, sizeof(FILE_LIST_ENTRY));
  766. if (pFileListEntry) {
  767. FileRecordNumberToSearchFor = pFileListEntry->FileRecordNumber;
  768. }
  769. else {
  770. FileRecordNumberToSearchFor = VolData.FileRecordNumber;
  771. }
  772. FileEntryToSearchFor.FileRecordNumber = FileRecordNumberToSearchFor;
  773. pClosestMatchEntry = (PFILE_LIST_ENTRY) RtlEnumerateGenericTableLikeADirectory(
  774. &VolData.BootOptimiseFileTable,
  775. NULL,
  776. NULL,
  777. FALSE,
  778. &pRestartKey,
  779. &ulDeleteCount,
  780. &FileEntryToSearchFor
  781. );
  782. if (!pClosestMatchEntry) {
  783. //
  784. // We couldn't find the closest match?
  785. //
  786. return FALSE;
  787. }
  788. if (pClosestMatchEntry->FileRecordNumber == FileRecordNumberToSearchFor) {
  789. //
  790. // We found an exact match. Update the fields of interest.
  791. //
  792. pClosestMatchEntry->StartingLcn =
  793. (pFileListEntry ? pFileListEntry->StartingLcn : VolData.StartingLcn);
  794. pClosestMatchEntry->ClusterCount = VolData.NumberOfClusters;
  795. // Get a pointer to the file extent header.
  796. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  797. //
  798. // Fill in the file info. We only count *excess* extents since
  799. // otherwise files with multiple streams would be "fragmented".
  800. //
  801. pClosestMatchEntry->ExcessExtentCount =
  802. (UINT)VolData.NumberOfFragments - pFileExtentHeader->NumberOfStreams;
  803. pClosestMatchEntry->Flags = 0;
  804. // Set or clear the fragmented and directory flags as needed
  805. if(VolData.bFragmented){
  806. //Set the fragmented flag.
  807. pClosestMatchEntry->Flags |= FLE_FRAGMENTED;
  808. }
  809. else{
  810. //Clear the fragmented flag.
  811. pClosestMatchEntry->Flags &= ~FLE_FRAGMENTED;
  812. }
  813. if(VolData.bDirectory){
  814. //Set the directory flag.
  815. pClosestMatchEntry->Flags |= FLE_DIRECTORY;
  816. }
  817. else{
  818. //Clear the directory flag.
  819. pClosestMatchEntry->Flags &= ~FLE_DIRECTORY;
  820. }
  821. pClosestMatchEntry->Flags |= FLE_BOOTOPTIMISE;
  822. VolData.bBootOptimiseFile = TRUE;
  823. VolData.BootOptimiseFileListTotalSize += VolData.NumberOfClusters;
  824. if ((!VolData.bFragmented) &&
  825. (VolData.StartingLcn >= VolData.BootOptimizeBeginClusterExclude) &&
  826. ((VolData.StartingLcn + VolData.NumberOfClusters) <= VolData.BootOptimizeEndClusterExclude)
  827. ) {
  828. VolData.BootOptimiseFilesAlreadyInZoneSize += VolData.NumberOfClusters;
  829. }
  830. //
  831. // We found and udpated this entry
  832. //
  833. if (!VolData.bFragmented) {
  834. return TRUE;
  835. }
  836. }
  837. //
  838. // We didn't find an exact match, or the file is fragmented
  839. //
  840. return FALSE;
  841. }
  842. /******************************************************************************
  843. ROUTINE DESCRIPTION:
  844. Moves the file referred to by VolData to a location outside the
  845. BootOptimise zone, if possible
  846. INPUT:
  847. (Global) Various VolData fields
  848. OUTPUT:
  849. None
  850. File referred to by VolData is moved to a new location outside the
  851. BootOptimise zone
  852. RETURN:
  853. TRUE if the file could successfully be moved
  854. FALSE otherwise
  855. */
  856. BOOL
  857. EvictFile(
  858. )
  859. {
  860. FILE_LIST_ENTRY NewFileListEntry; // entry for the file after the move
  861. FREE_SPACE_ENTRY NewFreeSpaceEntry; // entry for the free space after the move
  862. PRTL_GENERIC_TABLE pMoveToTable = NULL; // Table that will contain the file-entry after the move
  863. PRTL_GENERIC_TABLE pMoveFromTable = NULL; // Table that contains the file-entry before the move
  864. PVOID pvTemp = NULL; // Temporary pointer used for AVL-Tables
  865. BOOL bDone = FALSE;
  866. BOOL bResult = TRUE,
  867. bFragmented = VolData.bFragmented;
  868. BOOLEAN bNewElement = FALSE,
  869. bElementDeleted = FALSE;
  870. ZeroMemory(&NewFileListEntry, sizeof(FILE_LIST_ENTRY));
  871. ZeroMemory(&NewFreeSpaceEntry, sizeof(FREE_SPACE_ENTRY));
  872. //
  873. // If the file is fragmented, the entry should be present in the
  874. // FragementedFilesTable. If it isn't fragmented, the entry should be in
  875. // the ContiguousFileTable
  876. //
  877. pMoveFromTable = (VolData.bFragmented ?
  878. &VolData.FragmentedFileTable : &VolData.ContiguousFileTable);
  879. // Get the extent list & number of fragments in the file.
  880. if (GetExtentList(DEFAULT_STREAMS, NULL)) {
  881. bDone = FALSE;
  882. while (!bDone) {
  883. bDone = TRUE;
  884. if (FindSortedFreeSpace(&VolData.FreeSpaceTable)) {
  885. //
  886. // Found a free space chunk that was big enough. If it's
  887. // before the file, move the file towards the start of the disk
  888. //
  889. //
  890. // First, make a copy of the free-space and file-list entries,
  891. // and delete them from our tables. We'll add in modified
  892. // entries after the move.
  893. //
  894. CopyMemory(&NewFreeSpaceEntry,
  895. VolData.pFreeSpaceEntry,
  896. sizeof(FREE_SPACE_ENTRY)
  897. );
  898. bElementDeleted = RtlDeleteElementGenericTable(
  899. &VolData.FreeSpaceTable,
  900. (PVOID) VolData.pFreeSpaceEntry
  901. );
  902. if (!bElementDeleted) {
  903. Trace(warn, "Errors encountered while moving file. "
  904. "Could not find element in free space table. "
  905. "StartingLCN: %I64u ClusterCount: %I64u",
  906. NewFreeSpaceEntry.StartingLcn,
  907. NewFreeSpaceEntry.ClusterCount
  908. );
  909. assert(FALSE);
  910. }
  911. VolData.pFreeSpaceEntry = &NewFreeSpaceEntry;
  912. CopyMemory(&NewFileListEntry,
  913. VolData.pFileListEntry,
  914. sizeof(FILE_LIST_ENTRY)
  915. );
  916. bElementDeleted = RtlDeleteElementGenericTable(
  917. pMoveFromTable,
  918. (PVOID) VolData.pFileListEntry
  919. );
  920. if (bElementDeleted) {
  921. VolData.pFileListEntry = &NewFileListEntry;
  922. if (MoveNtfsFile()) {
  923. //
  924. // The file was successfully moved! Update our file-
  925. // and free-space entries with the results of the move.
  926. // We'll add these back to the appropriate trees in a bit.
  927. //
  928. NewFileListEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  929. VolData.pFreeSpaceEntry->StartingLcn += VolData.NumberOfClusters;
  930. VolData.pFreeSpaceEntry->ClusterCount -= VolData.NumberOfClusters;
  931. VolData.bFragmented = FALSE;
  932. VolData.pFileListEntry->Flags &= ~FLE_FRAGMENTED;
  933. //
  934. // Since we successfully moved (defragmented) this file,
  935. // it needs to be added to the ContiguousFilesTable
  936. //
  937. pMoveToTable = &VolData.ContiguousFileTable;
  938. if (UpdateInBootOptimiseList(&NewFileListEntry)) {
  939. //
  940. // Prevent this file from being counted twice
  941. //
  942. VolData.BootOptimiseFileListTotalSize -= VolData.NumberOfClusters;
  943. }
  944. }
  945. else {
  946. //
  947. // We could not move this file. Note that this could be
  948. // because of a number of reasons, such as:
  949. // 1. The free-space region is not really free
  950. // 2. The file is on the list of unmoveable files, etc
  951. //
  952. GetNtfsFilePath();
  953. Trace(warn, "Movefile failed. File %ws "
  954. "StartingLcn:%I64d ClusterCount:%I64d. Free-space "
  955. "StartingLcn:%I64d ClusterCount:%I64d Status:%lu",
  956. VolData.vFileName.GetBuffer() + 48,
  957. VolData.pFileListEntry->StartingLcn,
  958. VolData.pFileListEntry->ClusterCount,
  959. VolData.pFreeSpaceEntry->StartingLcn,
  960. VolData.pFreeSpaceEntry->ClusterCount,
  961. VolData.Status
  962. );
  963. if (VolData.Status == ERROR_RETRY) {
  964. //
  965. // Free space isn't really free; try again with
  966. // a different free space
  967. //
  968. VolData.pFreeSpaceEntry->ClusterCount = 0;
  969. bDone = FALSE;
  970. }
  971. //
  972. // Since we didn't move this file, we should just add
  973. // it back to the table it originally was in.
  974. //
  975. pMoveToTable = pMoveFromTable;
  976. }
  977. //
  978. // Add this file-entry back to the appropriate file-table
  979. //
  980. pvTemp = RtlInsertElementGenericTable(
  981. pMoveToTable,
  982. (PVOID) VolData.pFileListEntry,
  983. sizeof(FILE_LIST_ENTRY),
  984. &bNewElement);
  985. if (!pvTemp) {
  986. //
  987. // An allocation failed
  988. //
  989. Trace(warn, "Errors encountered while moving file: "
  990. "Unable to add back file-entry to file table");
  991. assert(FALSE);
  992. bResult = FALSE;
  993. break;
  994. }
  995. }
  996. if (VolData.pFreeSpaceEntry->ClusterCount > 0) {
  997. //
  998. // And also, add the (possibly updated) free-space region
  999. // to the FreeSpace list.
  1000. //
  1001. pvTemp = RtlInsertElementGenericTable(
  1002. &VolData.FreeSpaceTable,
  1003. (PVOID) VolData.pFreeSpaceEntry,
  1004. sizeof(FREE_SPACE_ENTRY),
  1005. &bNewElement);
  1006. if (!pvTemp) {
  1007. //
  1008. // An allocation failed
  1009. //
  1010. Trace(warn, "Errors encountered while moving file: "
  1011. "Unable to add back free-space to free-space table");
  1012. assert(FALSE);
  1013. bResult = FALSE;
  1014. break;
  1015. }
  1016. }
  1017. }
  1018. else {
  1019. //
  1020. // We could not find a free-space region big enough to move
  1021. // this file.
  1022. //
  1023. bResult = FALSE;
  1024. }
  1025. }
  1026. }
  1027. else {
  1028. //
  1029. // We could not get the extents for this file
  1030. //
  1031. bResult = FALSE;
  1032. }
  1033. //
  1034. // Clean-up
  1035. //
  1036. if(VolData.hFile != INVALID_HANDLE_VALUE) {
  1037. CloseHandle(VolData.hFile);
  1038. VolData.hFile = INVALID_HANDLE_VALUE;
  1039. }
  1040. if(VolData.hFreeExtents != NULL) {
  1041. while(GlobalUnlock(VolData.hFreeExtents))
  1042. ;
  1043. GlobalFree(VolData.hFreeExtents);
  1044. VolData.hFreeExtents = NULL;
  1045. }
  1046. // update cluster array
  1047. PurgeExtentBuffer();
  1048. return bResult;
  1049. }
  1050. /******************************************************************************
  1051. ROUTINE DESCRIPTION:
  1052. Moves the file referred to by VolData to a location closer to the start of
  1053. the disk, if possible
  1054. INPUT:
  1055. bForce - if the file is not currently in the boot-optimise zone, and has to
  1056. be moved forward.
  1057. (Global) Various VolData fields
  1058. OUTPUT:
  1059. None
  1060. File referred to by VolData is moved to a new location if possible
  1061. RETURN:
  1062. TRUE if the file could successfully be moved
  1063. FALSE otherwise
  1064. */
  1065. BOOL
  1066. MoveBootOptimiseFile(
  1067. IN CONST BOOL bForce
  1068. )
  1069. {
  1070. FILE_LIST_ENTRY NewFileListEntry; // entry for the file after the move
  1071. FREE_SPACE_ENTRY NewFreeSpaceEntry; // entry for the free space after the move
  1072. PRTL_GENERIC_TABLE pMoveToTable = NULL; // Table that will contain the file-entry after the move
  1073. PRTL_GENERIC_TABLE pMoveFromTable = NULL; // Table that contains the file-entry before the move
  1074. PVOID pvTemp = NULL; // Temporary pointer used for AVL-Tables
  1075. BOOL bDone = FALSE;
  1076. BOOL bResult = TRUE;
  1077. BOOLEAN bNewElement = FALSE,
  1078. bElementDeleted = FALSE;
  1079. ZeroMemory(&NewFileListEntry, sizeof(FILE_LIST_ENTRY));
  1080. ZeroMemory(&NewFreeSpaceEntry, sizeof(FREE_SPACE_ENTRY));
  1081. //
  1082. // If the file is fragmented, the entry should be present in the
  1083. // FragementedFilesTable. If it isn't fragmented, the entry should be in
  1084. // the ContiguousFileTable
  1085. //
  1086. pMoveFromTable = (VolData.bFragmented ?
  1087. &VolData.FragmentedFileTable : &VolData.ContiguousFileTable);
  1088. pMoveToTable = &VolData.ContiguousFileTable;
  1089. // Get the extent list & number of fragments in the file.
  1090. if (GetExtentList(DEFAULT_STREAMS, NULL)) {
  1091. bDone = FALSE;
  1092. while (!bDone) {
  1093. bDone = TRUE;
  1094. if (FindFreeSpaceWithMultipleTrees(VolData.NumberOfClusters,
  1095. (bForce ? VolData.TotalClusters : VolData.StartingLcn))
  1096. ) {
  1097. //
  1098. // Found a free space chunk that was big enough. If it's
  1099. // before the file, move the file towards the start of the disk
  1100. //
  1101. //
  1102. // First, make a copy of the free-space and file-list entries,
  1103. // and delete them from our tables. We'll add in modified
  1104. // entries after the move.
  1105. //
  1106. CopyMemory(&NewFileListEntry,
  1107. VolData.pFileListEntry,
  1108. sizeof(FILE_LIST_ENTRY)
  1109. );
  1110. bElementDeleted = RtlDeleteElementGenericTable(
  1111. pMoveFromTable,
  1112. (PVOID)VolData.pFileListEntry
  1113. );
  1114. if (bElementDeleted) {
  1115. VolData.pFileListEntry = &NewFileListEntry;
  1116. if (MoveNtfsFile()) {
  1117. //
  1118. // The file was successfully moved! Update our file-
  1119. // and free-space entries with the results of the move.
  1120. // We'll add these back to the appropriate trees in a bit.
  1121. //
  1122. NewFileListEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  1123. NewFreeSpaceEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn + VolData.NumberOfClusters;
  1124. NewFreeSpaceEntry.ClusterCount = VolData.pFreeSpaceEntry->ClusterCount - VolData.NumberOfClusters;
  1125. //
  1126. // Since we successfully moved (defragmented) this file,
  1127. // it needs to be added to the ContiguousFilesTable
  1128. //
  1129. pMoveToTable = &VolData.ContiguousFileTable;
  1130. //
  1131. // Update the free-space entry
  1132. //
  1133. UpdateInMultipleTrees(VolData.pFreeSpaceEntry, &NewFreeSpaceEntry);
  1134. VolData.pFreeSpaceEntry = NULL;
  1135. }
  1136. else {
  1137. //
  1138. // We could not move this file. Note that this could be
  1139. // because of a number of reasons, such as:
  1140. // 1. The free-space region is not really free
  1141. // 2. The file is on the list of unmoveable files, etc
  1142. //
  1143. GetNtfsFilePath();
  1144. Trace(warn, "Movefile failed. File %ws "
  1145. "StartingLcn:%I64d ClusterCount:%I64d. Free-space "
  1146. "StartingLcn:%I64d ClusterCount:%I64d Status:%lu",
  1147. VolData.vFileName.GetBuffer() + 48,
  1148. VolData.pFileListEntry->StartingLcn,
  1149. VolData.pFileListEntry->ClusterCount,
  1150. VolData.pFreeSpaceEntry->StartingLcn,
  1151. VolData.pFreeSpaceEntry->ClusterCount,
  1152. VolData.Status
  1153. );
  1154. if (VolData.Status == ERROR_RETRY) {
  1155. //
  1156. // Free space isn't really free; try again with
  1157. // a different free space
  1158. //
  1159. NewFreeSpaceEntry.StartingLcn = VolData.pFreeSpaceEntry->StartingLcn;
  1160. NewFreeSpaceEntry.ClusterCount = 0;
  1161. UpdateInMultipleTrees(VolData.pFreeSpaceEntry, NULL);
  1162. VolData.pFreeSpaceEntry = NULL;
  1163. bDone = FALSE;
  1164. }
  1165. //
  1166. // Since we didn't move this file, we should just add
  1167. // it back to the table it originally was in.
  1168. //
  1169. pMoveToTable = pMoveFromTable;
  1170. }
  1171. //
  1172. // Add this file-entry back to the appropriate file-table
  1173. //
  1174. pvTemp = RtlInsertElementGenericTable(
  1175. pMoveToTable,
  1176. (PVOID) VolData.pFileListEntry,
  1177. sizeof(FILE_LIST_ENTRY),
  1178. &bNewElement);
  1179. if (!pvTemp) {
  1180. //
  1181. // An allocation failed
  1182. //
  1183. Trace(warn, "Errors encountered while moving file: "
  1184. "Unable to add back file-entry to file table");
  1185. assert(FALSE);
  1186. bResult = FALSE;
  1187. break;
  1188. };
  1189. }
  1190. else {
  1191. bResult = TRUE;
  1192. }
  1193. }
  1194. else {
  1195. //
  1196. // We could not find a free-space region big enough to move
  1197. // this file.
  1198. //
  1199. if (bForce) {
  1200. GetNtfsFilePath();
  1201. Trace(warn, "Movefile failed: Insufficient free space. File %ws "
  1202. "StartingLcn:%I64d ClusterCount:%I64d FRN:%I64d Frag:%d Dir:%d",
  1203. VolData.vFileName.GetBuffer() + 48,
  1204. VolData.pFileListEntry->StartingLcn,
  1205. VolData.pFileListEntry->ClusterCount,
  1206. VolData.pFileListEntry->FileRecordNumber,
  1207. VolData.bFragmented, VolData.bDirectory
  1208. );
  1209. }
  1210. bResult = FALSE;
  1211. }
  1212. }
  1213. }
  1214. else {
  1215. //
  1216. // We could not get the extents for this file
  1217. //
  1218. if (bForce) {
  1219. GetNtfsFilePath();
  1220. Trace(warn, "Movefile failed: Unable to get extents. File %ws "
  1221. "StartingLcn:%I64d ClusterCount:%I64d FRN:%I64d Frag:%d Dir:%d",
  1222. VolData.vFileName.GetBuffer() + 48,
  1223. VolData.pFileListEntry->StartingLcn,
  1224. VolData.pFileListEntry->ClusterCount,
  1225. VolData.pFileListEntry->FileRecordNumber,
  1226. VolData.bFragmented, VolData.bDirectory
  1227. );
  1228. }
  1229. bResult = FALSE;
  1230. }
  1231. //
  1232. // Clean-up
  1233. //
  1234. if(VolData.hFile != INVALID_HANDLE_VALUE) {
  1235. CloseHandle(VolData.hFile);
  1236. VolData.hFile = INVALID_HANDLE_VALUE;
  1237. }
  1238. if(VolData.hFreeExtents != NULL) {
  1239. while(GlobalUnlock(VolData.hFreeExtents))
  1240. ;
  1241. GlobalFree(VolData.hFreeExtents);
  1242. VolData.hFreeExtents = NULL;
  1243. }
  1244. // update cluster array
  1245. PurgeExtentBuffer();
  1246. return bResult;
  1247. }
  1248. /******************************************************************************
  1249. ROUTINE DESCRIPTION:
  1250. Gets the start and end markers for the Boot-Optimise region from the
  1251. registry
  1252. INPUT:
  1253. lpBootOptimiseKey - The key to read
  1254. RETURN:
  1255. The value at the specified key, 0 if errors were encountered
  1256. */
  1257. LONGLONG
  1258. GetStartingEndLcnLocations(
  1259. IN PTCHAR lpBootOptimiseKey
  1260. )
  1261. {
  1262. HKEY hValue = NULL; //hkey for the registry value
  1263. DWORD dwRegValueSize = 0; //size of the registry value string
  1264. long ret = 0; //return value from SetRegValue
  1265. TCHAR cRegValue[100]; //string to hold the value for the registry
  1266. LONGLONG lLcnStartEndLocation = 0;
  1267. //get the LcnStartLocation from the registry
  1268. dwRegValueSize = sizeof(cRegValue);
  1269. ret = GetRegValue(
  1270. &hValue,
  1271. BOOT_OPTIMIZE_REGISTRY_PATH,
  1272. lpBootOptimiseKey,
  1273. cRegValue,
  1274. &dwRegValueSize);
  1275. RegCloseKey(hValue);
  1276. //check to see if the key exists, else exit from routine
  1277. if (ret != ERROR_SUCCESS) {
  1278. hValue = NULL;
  1279. _stprintf(cRegValue,TEXT("%d"),0);
  1280. //add the LcnStartLocation to the registry
  1281. dwRegValueSize = sizeof(cRegValue);
  1282. ret = SetRegValue(
  1283. &hValue,
  1284. BOOT_OPTIMIZE_REGISTRY_PATH,
  1285. lpBootOptimiseKey,
  1286. cRegValue,
  1287. dwRegValueSize,
  1288. REG_SZ);
  1289. RegCloseKey(hValue);
  1290. }
  1291. else {
  1292. lLcnStartEndLocation = _ttoi(cRegValue);
  1293. }
  1294. return lLcnStartEndLocation;
  1295. }
  1296. /*****************************************************************************************************************
  1297. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1298. ROUTINE DESCRIPTION:
  1299. Gets the registry entries at the beginning of the program
  1300. INPUT:
  1301. Success - TRUE
  1302. Failed - FALSE
  1303. RETURN:
  1304. None
  1305. */
  1306. BOOL GetRegistryEntries(
  1307. OUT TCHAR lpLayoutIni[MAX_PATH]
  1308. )
  1309. {
  1310. HKEY hValue = NULL; //hkey for the registry value
  1311. DWORD dwRegValueSize = 0; //size of the registry value string
  1312. long ret = 0; //return value from SetRegValue
  1313. TCHAR cEnabledString[2]; //holds the enabled flag
  1314. // get Boot Optimize file name from registry
  1315. dwRegValueSize = sizeof(cEnabledString);
  1316. ret = GetRegValue(
  1317. &hValue,
  1318. BOOT_OPTIMIZE_REGISTRY_PATH,
  1319. BOOT_OPTIMIZE_ENABLE_FLAG,
  1320. cEnabledString,
  1321. &dwRegValueSize);
  1322. RegCloseKey(hValue);
  1323. //check to see if the key exists, else exit from routine
  1324. if (ret != ERROR_SUCCESS)
  1325. {
  1326. return FALSE;
  1327. }
  1328. //check to see that boot optimize is enabled
  1329. if(cEnabledString[0] != TEXT('Y'))
  1330. {
  1331. return FALSE;
  1332. }
  1333. // get Boot Optimize file name from registry
  1334. hValue = NULL;
  1335. dwRegValueSize = MAX_PATH;
  1336. ret = GetRegValue(
  1337. &hValue,
  1338. OPTIMAL_LAYOUT_KEY_PATH,
  1339. OPTIMAL_LAYOUT_FILE_VALUE_NAME,
  1340. lpLayoutIni,
  1341. &dwRegValueSize);
  1342. RegCloseKey(hValue);
  1343. //check to see if the key exists, else exit from routine
  1344. if (ret != ERROR_SUCCESS)
  1345. {
  1346. return FALSE;
  1347. }
  1348. return TRUE;
  1349. }
  1350. /******************************************************************************
  1351. ROUTINE DESCRIPTION:
  1352. Initialisation routine for the BootOptimisation.
  1353. INPUT/OUTPUT:
  1354. Various VolData fields
  1355. RETURN:
  1356. TRUE - Initialisation completed successfully
  1357. FALSE - Fatal errors were encountered during initialisation
  1358. */
  1359. BOOL
  1360. InitialiseBootOptimise(
  1361. IN CONST BOOL bIsNtfs
  1362. )
  1363. {
  1364. LONGLONG lLcnStartLocation = 0; //the starting location of where the files were moved last
  1365. LONGLONG lLcnEndLocation = 0; //the ending location of where the files were moved last
  1366. LONGLONG lLcnMinEndLocation = 0; //the minimum size the BootOptimise zone needs to be
  1367. TCHAR lpLayoutIni[MAX_PATH]; //string to hold the path of the file
  1368. LONGLONG ClustersNeededForLayout = 0; //size in clusters of how big the boot optimize files are
  1369. BOOL bResult = FALSE;
  1370. Trace(log, "Start: Initialising BootOptimise. Volume %c:", VolData.cDrive);
  1371. // Initialise the tree to hold the layout.ini entries
  1372. bResult = InitialiseBootOptimiseTables(&VolData.BootOptimiseFileTable,
  1373. &VolData.FilesInBootExcludeZoneTable);
  1374. if (!bResult) {
  1375. SaFreeContext(&VolData.SaBootOptimiseFilesContext);
  1376. Trace(log, "End: Initialising BootOptimise. Out of memory");
  1377. SaveErrorInRegistry(TEXT("No"),TEXT("Insufficient Resources"));
  1378. return FALSE;
  1379. }
  1380. //get the registry entries
  1381. bResult = GetRegistryEntries(lpLayoutIni);
  1382. if(!bResult) {
  1383. Trace(log, "End: Initialising BootOptimise. Missing registry entries");
  1384. SaveErrorInRegistry(TEXT("No"),TEXT("Missing Registry Entries"));
  1385. return FALSE; //must be some error in getting registry entries
  1386. }
  1387. // Get the start and end goalposts for our boot-optimize region
  1388. lLcnStartLocation = GetStartingEndLcnLocations(BOOT_OPTIMIZE_REGISTRY_LCNSTARTLOCATION);
  1389. lLcnEndLocation = GetStartingEndLcnLocations(BOOT_OPTIMIZE_REGISTRY_LCNENDLOCATION);
  1390. // And build the list of files to be boot optimised
  1391. bResult = BuildBootOptimiseFileList(
  1392. &VolData.BootOptimiseFileTable,
  1393. lpLayoutIni,
  1394. VolData.cDrive,
  1395. bIsNtfs,
  1396. &ClustersNeededForLayout);
  1397. if (!bResult) {
  1398. SaFreeContext(&VolData.SaBootOptimiseFilesContext);
  1399. Trace(log, "End: Initialising BootOptimise. Out of memory");
  1400. SaveErrorInRegistry(TEXT("No"),TEXT("Insufficient Resources"));
  1401. return FALSE;
  1402. }
  1403. //
  1404. // If there are files in the "boot optimise zone" that are not in our layout.ini
  1405. // list, we shall evict them if possible.
  1406. //
  1407. lLcnMinEndLocation = lLcnStartLocation + ClustersNeededForLayout;
  1408. if (lLcnMinEndLocation > lLcnEndLocation) {
  1409. lLcnEndLocation = lLcnMinEndLocation;
  1410. }
  1411. VolData.BootOptimizeBeginClusterExclude = lLcnStartLocation;
  1412. VolData.BootOptimizeEndClusterExclude = lLcnEndLocation;
  1413. Trace(log, "End: Initialising BootOptimise. Zone Begins %I64d, Ends %I64d (%I64d clusters, Minimum needed: %I64d clusters).",
  1414. VolData.BootOptimizeBeginClusterExclude,
  1415. VolData.BootOptimizeEndClusterExclude,
  1416. VolData.BootOptimizeEndClusterExclude - VolData.BootOptimizeBeginClusterExclude,
  1417. ClustersNeededForLayout
  1418. );
  1419. return TRUE;
  1420. }
  1421. /******************************************************************************
  1422. ROUTINE DESCRIPTION:
  1423. Routine for BootOptimisation.
  1424. INPUT/OUTPUT:
  1425. Various VolData fields
  1426. RETURN:
  1427. ENG_NOERR on success; appropriate ENGERR failure codes otherwise
  1428. */
  1429. DWORD
  1430. ProcessBootOptimise(
  1431. )
  1432. {
  1433. BOOLEAN bRestart = TRUE;
  1434. BOOL bResult = FALSE;
  1435. BOOL bForce = FALSE,
  1436. bRelocateZone = FALSE;
  1437. LONGLONG llBiggestFreeSpaceRegionStartingLcn = 0,
  1438. llBiggestFreeSpaceRegionClusterCount = 0,
  1439. llAdditionalClustersNeeded = 0,
  1440. llMaxBootClusterEnd = 0;
  1441. LONGLONG llTotalClustersToBeMoved = 0,
  1442. llClustersSuccessfullyMoved = 0;
  1443. FILE_LIST_ENTRY NextFileEntry;
  1444. DWORD dwStatus = ENG_NOERR,
  1445. dwIndex = 0;
  1446. Trace(log, "Start: Processing BootOptimise");
  1447. ZeroMemory(&NextFileEntry, sizeof(FILE_LIST_ENTRY));
  1448. if (!VolData.BootOptimizeEndClusterExclude) {
  1449. Trace(log, "End: Processing BootOptimise. BootOptimise region "
  1450. "uninitialised (not boot volume?)");
  1451. return ENGERR_BAD_PARAM;
  1452. }
  1453. // Exit if the controller wants us to stop.
  1454. if (TERMINATE == VolData.EngineState) {
  1455. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  1456. ExitThread(0);
  1457. }
  1458. //
  1459. // At this point, VolData.BootOptimiseFileTable contains a copy of the file
  1460. // records for the files that need to be boot-optimised, and
  1461. // VolData.FilesInBootExcludeZoneTable contains the files that are in our
  1462. // preferred boot-optimise zone that need to be evicted
  1463. //
  1464. //
  1465. // Build the free space list, excluding the zone that we are interested in.
  1466. //
  1467. bResult = BuildFreeSpaceList(
  1468. &VolData.FreeSpaceTable,
  1469. 0,
  1470. TRUE,
  1471. &llBiggestFreeSpaceRegionClusterCount,
  1472. &llBiggestFreeSpaceRegionStartingLcn,
  1473. TRUE
  1474. );
  1475. if (!bResult) {
  1476. Trace(log, "End: Processing BootOptimise. Errors encountered while determining free space");
  1477. return ENGERR_NOMEM;
  1478. }
  1479. Trace(log, "BiggestCluster LCN %I64u (%I64u clusters). "
  1480. "BootOptimiseFilesTotalSize:%I64u AlreadyInZoneSize:%I64u (%d%%)",
  1481. llBiggestFreeSpaceRegionStartingLcn, llBiggestFreeSpaceRegionClusterCount,
  1482. VolData.BootOptimiseFileListTotalSize,
  1483. VolData.BootOptimiseFilesAlreadyInZoneSize,
  1484. VolData.BootOptimiseFilesAlreadyInZoneSize * 100 / VolData.BootOptimiseFileListTotalSize
  1485. );
  1486. if (llBiggestFreeSpaceRegionClusterCount > VolData.BootOptimiseFileListTotalSize) {
  1487. //
  1488. // There is a free space region that is bigger than the total files
  1489. // we want to move--so check if we want to relocate the boot-optimise
  1490. // zone.
  1491. //
  1492. if ((VolData.BootOptimiseFilesAlreadyInZoneSize * 100 / VolData.BootOptimiseFileListTotalSize) < BOOT_OPTIMISE_ZONE_RELOCATE_THRESHOLD) {
  1493. //
  1494. // Less than 90% of the Boot-Optimise files are already in the zone.
  1495. //
  1496. Trace(log, "Relocating boot-optimise zone to LCN %I64u (%I64u clusters free). "
  1497. "BootOptimiseFilesTotalSize:%I64u AlreadyInZoneSize:%I64u (%d%%)",
  1498. llBiggestFreeSpaceRegionStartingLcn, llBiggestFreeSpaceRegionClusterCount,
  1499. VolData.BootOptimiseFileListTotalSize,
  1500. VolData.BootOptimiseFilesAlreadyInZoneSize,
  1501. VolData.BootOptimiseFilesAlreadyInZoneSize * 100 / VolData.BootOptimiseFileListTotalSize
  1502. );
  1503. bRelocateZone = TRUE;
  1504. VolData.BootOptimizeBeginClusterExclude = llBiggestFreeSpaceRegionStartingLcn;
  1505. VolData.BootOptimizeEndClusterExclude = VolData.BootOptimizeBeginClusterExclude + VolData.BootOptimiseFileListTotalSize;
  1506. }
  1507. }
  1508. if (!bRelocateZone) {
  1509. //
  1510. // Go through the VolData.FilesInBootExcludeZoneTable, and evict them.
  1511. //
  1512. bRestart = TRUE;
  1513. do {
  1514. // Exit if the controller wants us to stop.
  1515. if (TERMINATE == VolData.EngineState) {
  1516. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  1517. return ENGERR_GENERAL;
  1518. }
  1519. bResult = GetNextNtfsFile(&VolData.FilesInBootExcludeZoneTable, bRestart);
  1520. bRestart = FALSE;
  1521. if (bResult) {
  1522. llTotalClustersToBeMoved += VolData.NumberOfClusters;
  1523. if (EvictFile()) {
  1524. llClustersSuccessfullyMoved += VolData.NumberOfClusters;
  1525. }
  1526. }
  1527. } while (bResult);
  1528. }
  1529. Trace(log, "%I64d of %I64d clusters successfully evicted (%d%%)",
  1530. llClustersSuccessfullyMoved, llTotalClustersToBeMoved,
  1531. (llTotalClustersToBeMoved > 0 ?
  1532. (llClustersSuccessfullyMoved * 100 / llTotalClustersToBeMoved) : 0));
  1533. llClustersSuccessfullyMoved = 0;
  1534. llTotalClustersToBeMoved = VolData.BootOptimiseFileListTotalSize;
  1535. //
  1536. // The next step is to move files from layout.ini to the boot optimise
  1537. // region. First build a new free-space list, sorted by startingLcn.
  1538. //
  1539. ClearFreeSpaceTable();
  1540. AllocateFreeSpaceListsWithMultipleTrees();
  1541. bResult = BuildFreeSpaceListWithMultipleTrees(
  1542. &llBiggestFreeSpaceRegionClusterCount,
  1543. VolData.BootOptimizeBeginClusterExclude,
  1544. VolData.BootOptimizeEndClusterExclude);
  1545. if (!bResult) {
  1546. Trace(log, "End: Processing BootOptimise. Errors encountered while determining free space");
  1547. return ENGERR_NOMEM;
  1548. }
  1549. //
  1550. // Finally go through VolData.BootOptmiseFileTable, and move the files
  1551. // to the boot-optimise region. This will also move files forward if needed.
  1552. //
  1553. if (VolData.pBootOptimiseFrnList) {
  1554. do {
  1555. NextFileEntry.FileRecordNumber = VolData.pBootOptimiseFrnList[dwIndex];
  1556. dwIndex++;
  1557. if (NextFileEntry.FileRecordNumber < 0) {
  1558. bResult = FALSE;
  1559. }
  1560. else {
  1561. bResult = GetNextNtfsFile(&VolData.BootOptimiseFileTable, TRUE, 0, &NextFileEntry);
  1562. }
  1563. // Exit if the controller wants us to stop.
  1564. if (TERMINATE == VolData.EngineState) {
  1565. PostMessage(hwndMain, WM_CLOSE, 0, 0);
  1566. return ENGERR_GENERAL;
  1567. }
  1568. if (bResult) {
  1569. if (VolData.FileRecordNumber == 0) {
  1570. //
  1571. // Ignore the MFT
  1572. //
  1573. continue;
  1574. }
  1575. //
  1576. // We should only move files less than BOOT_OPTIMIZE_MAX_FILE_SIZE_BYTES MB
  1577. //
  1578. if ((VolData.NumberOfClusters == 0) ||
  1579. (VolData.NumberOfClusters > (BOOT_OPTIMIZE_MAX_FILE_SIZE_BYTES / VolData.BytesPerCluster))) {
  1580. continue;
  1581. }
  1582. //
  1583. // Ignore files that we couldn't find during the analyse phase
  1584. //
  1585. if (VolData.StartingLcn == VolData.TotalClusters) {
  1586. continue;
  1587. }
  1588. if (VolData.bFragmented == TRUE) {
  1589. bForce = TRUE;
  1590. }
  1591. else {
  1592. if ((!VolData.bFragmented) &&
  1593. (VolData.StartingLcn >= VolData.BootOptimizeBeginClusterExclude) &&
  1594. ((VolData.StartingLcn + VolData.NumberOfClusters) <= VolData.BootOptimizeEndClusterExclude)
  1595. ) {
  1596. //
  1597. // File is fully contained in the boot-optimise zone. Let's just
  1598. // try to move it forward if possible, but no worries if we can't.
  1599. //
  1600. bForce = FALSE;
  1601. }
  1602. else {
  1603. bForce = TRUE;
  1604. }
  1605. }
  1606. if (MoveBootOptimiseFile(bForce)) {
  1607. llClustersSuccessfullyMoved += VolData.NumberOfClusters;
  1608. }
  1609. else {
  1610. if (bForce) {
  1611. dwStatus = ENGERR_RETRY;
  1612. llAdditionalClustersNeeded += VolData.NumberOfClusters;
  1613. }
  1614. }
  1615. if ((VolData.StartingLcn + VolData.NumberOfClusters > llMaxBootClusterEnd) &&
  1616. (VolData.StartingLcn <= VolData.TotalClusters)){
  1617. llMaxBootClusterEnd = VolData.StartingLcn + VolData.NumberOfClusters;
  1618. }
  1619. }
  1620. } while (bResult);
  1621. }
  1622. else {
  1623. dwStatus = ENGERR_NOMEM;
  1624. }
  1625. Trace(log, "%I64d of %I64d clusters successfully moved to zone (%d%%).",
  1626. llClustersSuccessfullyMoved, llTotalClustersToBeMoved,
  1627. (llTotalClustersToBeMoved > 0 ?
  1628. (llClustersSuccessfullyMoved * 100 / llTotalClustersToBeMoved) : 0)
  1629. );
  1630. //
  1631. // Clean-up
  1632. //
  1633. ClearFreeSpaceListWithMultipleTrees();
  1634. if (VolData.hBootOptimiseFrnList != NULL) {
  1635. while (GlobalUnlock(VolData.hBootOptimiseFrnList)) {
  1636. Sleep(1000);
  1637. }
  1638. GlobalFree(VolData.hBootOptimiseFrnList);
  1639. VolData.hBootOptimiseFrnList = NULL;
  1640. VolData.pBootOptimiseFrnList = NULL;
  1641. }
  1642. if (ENGERR_RETRY == dwStatus) {
  1643. //
  1644. // Some files could not be moved--we need to grow the boot-optimise zone
  1645. // and retry.
  1646. //
  1647. //
  1648. // Make sure the boot-optimise zone isn't more than 4GB, and 50% of the
  1649. // disk, whichever is smaller
  1650. //
  1651. if (
  1652. ((VolData.BootOptimizeEndClusterExclude - VolData.BootOptimizeBeginClusterExclude) >
  1653. ((LONGLONG) BOOT_OPTIMIZE_MAX_ZONE_SIZE_MB * ((LONGLONG) 1024 * 1024 / (LONGLONG) VolData.BytesPerCluster))) ||
  1654. ((VolData.BootOptimizeEndClusterExclude - VolData.BootOptimizeBeginClusterExclude) >
  1655. (VolData.TotalClusters * BOOT_OPTIMIZE_MAX_ZONE_SIZE_PERCENT / 100))
  1656. ) {
  1657. dwStatus = ENGERR_LOW_FREESPACE;
  1658. }
  1659. else {
  1660. if (llAdditionalClustersNeeded < (BOOT_OPTIMIZE_ZONE_EXTEND_MIN_SIZE_BYTES / VolData.BytesPerCluster)) {
  1661. llAdditionalClustersNeeded = BOOT_OPTIMIZE_ZONE_EXTEND_MIN_SIZE_BYTES / VolData.BytesPerCluster;
  1662. }
  1663. VolData.BootOptimizeEndClusterExclude += (llAdditionalClustersNeeded *
  1664. BOOT_OPTIMIZE_ZONE_EXTEND_PERCENT / 100);
  1665. }
  1666. }
  1667. else if (ENG_NOERR == dwStatus) {
  1668. VolData.BootOptimizeEndClusterExclude = llMaxBootClusterEnd;
  1669. }
  1670. SetRegistryEntires(VolData.BootOptimizeBeginClusterExclude,
  1671. VolData.BootOptimizeEndClusterExclude);
  1672. UnInitialiseBootOptimiseTables(&VolData.BootOptimiseFileTable,
  1673. &VolData.FilesInBootExcludeZoneTable);
  1674. if (ENG_NOERR == dwStatus) {
  1675. SaveErrorInRegistry(TEXT("Yes"),TEXT(" "));
  1676. Trace(log, "End: Processing BootOptimise. Done");
  1677. }
  1678. else {
  1679. SaveErrorInRegistry(TEXT("No"),TEXT("Insufficient free space"));
  1680. Trace(log, "End: Processing BootOptimise. Insufficient free space");
  1681. }
  1682. return dwStatus;
  1683. }
  1684. /*****************************************************************************************************************
  1685. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1686. ROUTINE DESCRIPTION:
  1687. Set the registry entries at the end
  1688. INPUT:
  1689. None
  1690. RETURN:
  1691. None
  1692. */
  1693. VOID SetRegistryEntires(
  1694. IN LONGLONG lLcnStartLocation,
  1695. IN LONGLONG lLcnEndLocation
  1696. )
  1697. {
  1698. HKEY hValue = NULL; //hkey for the registry value
  1699. DWORD dwRegValueSize = 0; //size of the registry value string
  1700. long ret = 0; //return value from SetRegValue
  1701. TCHAR cRegValue[100]; //string to hold the value for the registry
  1702. _stprintf(cRegValue,TEXT("%I64d"),lLcnStartLocation);
  1703. //set the LcnEndLocation from the registry
  1704. dwRegValueSize = sizeof(cRegValue);
  1705. ret = SetRegValue(
  1706. &hValue,
  1707. BOOT_OPTIMIZE_REGISTRY_PATH,
  1708. BOOT_OPTIMIZE_REGISTRY_LCNSTARTLOCATION,
  1709. cRegValue,
  1710. dwRegValueSize,
  1711. REG_SZ);
  1712. RegCloseKey(hValue);
  1713. hValue = NULL;
  1714. _stprintf(cRegValue,TEXT("%I64d"),lLcnEndLocation);
  1715. //set the LcnEndLocation from the registry
  1716. dwRegValueSize = sizeof(cRegValue);
  1717. ret = SetRegValue(
  1718. &hValue,
  1719. BOOT_OPTIMIZE_REGISTRY_PATH,
  1720. BOOT_OPTIMIZE_REGISTRY_LCNENDLOCATION,
  1721. cRegValue,
  1722. dwRegValueSize,
  1723. REG_SZ);
  1724. RegCloseKey(hValue);
  1725. }
  1726. /*****************************************************************************************************************
  1727. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1728. ROUTINE DESCRIPTION:
  1729. Save the error that may have occured in the registry
  1730. INPUT:
  1731. TCHAR tComplete Set to Y when everything worked, set to N when error
  1732. TCHAR* tErrorString A description of what error occured.
  1733. RETURN:
  1734. None
  1735. */
  1736. VOID SaveErrorInRegistry(
  1737. TCHAR* tComplete,
  1738. TCHAR* tErrorString)
  1739. {
  1740. HKEY hValue = NULL; //hkey for the registry value
  1741. DWORD dwRegValueSize = 0; //size of the registry value string
  1742. long ret = 0; //return value from SetRegValue
  1743. //set the error code of the error in the registry
  1744. dwRegValueSize = 2*(_tcslen(tErrorString));
  1745. ret = SetRegValue(
  1746. &hValue,
  1747. BOOT_OPTIMIZE_REGISTRY_PATH,
  1748. BOOT_OPTIMIZE_REGISTRY_ERROR,
  1749. tErrorString,
  1750. dwRegValueSize,
  1751. REG_SZ);
  1752. RegCloseKey(hValue);
  1753. //set the error status in the registry
  1754. hValue = NULL;
  1755. dwRegValueSize = 2*(_tcslen(tComplete));
  1756. ret = SetRegValue(
  1757. &hValue,
  1758. BOOT_OPTIMIZE_REGISTRY_PATH,
  1759. BOOT_OPTIMIZE_REGISTRY_COMPLETE,
  1760. tComplete,
  1761. dwRegValueSize,
  1762. REG_SZ);
  1763. RegCloseKey(hValue);
  1764. }
  1765. /*****************************************************************************************************************
  1766. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1767. ROUTINE DESCRIPTION:
  1768. Get the date/time stamp of the input file
  1769. INPUT:
  1770. full path to the boot optimize file
  1771. RETURN:
  1772. TRUE if file time does not match what is in the registry
  1773. FALSE if the file time matches what is in the registry
  1774. */
  1775. BOOL CheckDateTimeStampInputFile(
  1776. IN TCHAR cBootOptimzePath[MAX_PATH]
  1777. )
  1778. {
  1779. WIN32_FILE_ATTRIBUTE_DATA extendedAttr; //structure to hold file attributes
  1780. LARGE_INTEGER tBootOptimeFileTime; //holds the last write time of the file
  1781. LARGE_INTEGER tBootOptimeRegistryFileTime; //holds the last write time of the file from registry
  1782. HKEY hValue = NULL; //hkey for the registry value
  1783. DWORD dwRegValueSize = 0; //size of the registry value string
  1784. long ret = 0; //return value from SetRegValue
  1785. tBootOptimeFileTime.LowPart = 0;
  1786. tBootOptimeFileTime.HighPart = 0;
  1787. tBootOptimeRegistryFileTime.LowPart = 0;
  1788. tBootOptimeRegistryFileTime.HighPart = 0;
  1789. //get the last write time of the file
  1790. //if it fails, return FALSE
  1791. if (GetFileAttributesEx (cBootOptimzePath,
  1792. GetFileExInfoStandard,
  1793. &extendedAttr))
  1794. {
  1795. tBootOptimeFileTime.LowPart = extendedAttr.ftLastWriteTime.dwLowDateTime;
  1796. tBootOptimeFileTime.HighPart = extendedAttr.ftLastWriteTime.dwHighDateTime;
  1797. } else
  1798. {
  1799. return TRUE; //some error happened and we exit and say we cant get the file time
  1800. }
  1801. //get the time from the registry
  1802. hValue = NULL;
  1803. dwRegValueSize = sizeof(tBootOptimeFileTime.QuadPart);
  1804. ret = GetRegValue(
  1805. &hValue,
  1806. BOOT_OPTIMIZE_REGISTRY_PATH,
  1807. BOOT_OPTIMIZE_LAST_WRITTEN_DATETIME,
  1808. &(LONGLONG)tBootOptimeRegistryFileTime.QuadPart,
  1809. &dwRegValueSize);
  1810. RegCloseKey(hValue);
  1811. //check to see if the key exists, if it does, check to see if the date/time stamp
  1812. //matches, if it does, exit else write a registry entry
  1813. if (ret == ERROR_SUCCESS)
  1814. {
  1815. if(tBootOptimeFileTime.QuadPart == tBootOptimeRegistryFileTime.QuadPart)
  1816. {
  1817. return FALSE; //the file times matched and we exit
  1818. }
  1819. }
  1820. hValue = NULL;
  1821. //update the date and time of the bootoptimize file to the registry
  1822. dwRegValueSize = sizeof(tBootOptimeFileTime.QuadPart);
  1823. ret = SetRegValue(
  1824. &hValue,
  1825. BOOT_OPTIMIZE_REGISTRY_PATH,
  1826. BOOT_OPTIMIZE_LAST_WRITTEN_DATETIME,
  1827. (LONGLONG)tBootOptimeFileTime.QuadPart,
  1828. dwRegValueSize,
  1829. REG_QWORD);
  1830. RegCloseKey(hValue);
  1831. return TRUE;
  1832. }