/***************************************************************************************************************** FILENAME: MoveFile.cpp COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc. */ #include "stdafx.h" #include #include extern "C" { #include "SysStruc.h" } #include "ErrMacro.h" #include "DfrgCmn.h" #include "DfrgEngn.h" #include "DfrgRes.h" #include "Devio.h" #include "Extents.h" #include "FreeSpace.h" #include "MoveFile.h" #include "NtfsSubs.h" #include "Alloc.h" #include "DiskView.h" #include "Event.h" #include "Logging.h" #include "FsSubs.h" #define THIS_MODULE 'O' #include "logfile.h" static void UpdatePostMoveStats(FILE_EXTENT_HEADER* pFileExtentHeader, LONGLONG llBeforeExtents); /***************************************************************************************************************** COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc. ROUTINE DESCRIPTION: Moves a file to a new location on a disk (so as to defrag it). GLOBALS: VolData RETURN: TRUE = Success FALSE = Failure */ BOOL MoveFile( ) { TCHAR cString[500]; UINT i, j; FILE_EXTENT_HEADER* pFileExtentHeader; STREAM_EXTENT_HEADER* pStreamExtentHeader; EXTENT_LIST* pExtents; LONGLONG VirtualFileClusters; LONGLONG llBeforeExtents; ATTRIBUTE_TYPE_CODE TypeCode = 0; LONGLONG Vcn = 0; LONGLONG Lcn = VolData.FoundLcn; LONGLONG RunLength = VolData.FoundLen; LONGLONG oldStartingLcn = 0; BOOL bReturnValue = FALSE; // assume error BOOL bFileStartedFragmented; // keeps track of the starting state of a file // Validate that we have a file system. if((VolData.FileSystem != FS_NTFS) && (VolData.FileSystem != FS_FAT) && (VolData.FileSystem != FS_FAT32)) { EF_ASSERT(FALSE); } // Debug // ShowExtentList(); // Set the status to move the next file. Therefore, unless this variable gets reset // to NEXT_ALGO_STEP within this function, it goes on to the next file by default. #ifdef DFRGNTFS VolData.Status = ERROR_SUCCESS; #elif DFRGFAT VolData.Status = NEXT_FILE; #endif // Set up the Extent pointers structure to fill in the extent list in VolData. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList; pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER)); pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER)); //Scott Sipe 5/1/2000 Boot Optimize //we now have the extents for the file, it the file is part of the files that have been //moved by the BootOptimize routine, then we do not want to move them, so just test to see //if the pExtents->StartingLcn is in the block of optimized files, then exit the routine and //return FALSE #ifdef DFRGFAT if((VolData.BootOptimizeBeginClusterExclude != 0 || VolData.BootOptimizeEndClusterExclude != 0) && IsBootVolume(VolData.cDrive)) { if(pExtents->StartingLcn >= (LONGLONG)VolData.BootOptimizeBeginClusterExclude && pExtents->StartingLcn < (LONGLONG)VolData.BootOptimizeEndClusterExclude) { return(FALSE); } } #endif __try { // Mark the file as free space before we move it. After we move it, // the new blocks will be colored in with the correct color. if (!AddExtents(FreeSpaceColor)){ EH(FALSE); __leave; } // Record the number of extents in the stream before we move it. llBeforeExtents = pFileExtentHeader->ExcessExtents; // Loop through each stream in the file and move them. for(i=0; iNumberOfStreams; i++) { // If this is not the first stream, then get a handle to the current stream. // We already have a handle to the first stream when this function is called. if(i) { #ifdef DFRGNTFS TCHAR StreamName[MAX_PATH]; // Get this stream's streamname. if (!GetStreamNameAndTypeFromNumber(i, StreamName, &TypeCode, NULL)){ EH(FALSE); __leave; } // save a copy of the file name so that we can reset it later PTCHAR strFileName = new TCHAR[VolData.vFileName.GetLength()+1]; if ((strFileName == NULL) || VolData.vFileName.IsEmpty()) { if (strFileName) { delete [] strFileName; strFileName = NULL; } EH(FALSE); __leave; } _tcscpy(strFileName, VolData.vFileName.GetBuffer()); // Append this stream's filename to the end of the file's name. // Note, if there was already another stream's name appended, then // this will simply overwrite it directly. This puts the colon // between the filename and the streamname. VolData.vFileName.AddChar(L':'); // This copies streamname plus terminator to the end. VolData.vFileName += StreamName; // Opens the stream. if (!OpenNtfsFile()){ delete [] strFileName; strFileName = NULL; EH(FALSE); __leave; } // reset it (remove the stream name) so that the next stream will work ok VolData.vFileName = strFileName; delete [] strFileName; strFileName = NULL; #else // If this is not NTFS, then there can't be multiple streams! EH_ASSERT(FALSE); __leave; #endif } // Calculate how many clusters there are virtually in this stream // (more than there are on the disk if this is a compressed file). if (VolData.BytesPerCluster == 0){ EH_ASSERT(FALSE); __leave; } VirtualFileClusters = pStreamExtentHeader->AllocatedLength / VolData.BytesPerCluster; if (pStreamExtentHeader->AllocatedLength % VolData.BytesPerCluster) { VirtualFileClusters++; } // Say where we're moving the file to. wsprintf(cString, TEXT("Moving file %#lx at Vcn %#lx to Lcn %#lx for %#lx"), (ULONG)VolData.FileRecordNumber, (ULONG)Vcn, (ULONG)Lcn, (ULONG)VirtualFileClusters); Message(cString, -1, NULL); // stats for move attempts if (VolData.bFragmented){ VolData.FragmentedFileMovesAttempted[VolData.Pass]++; bFileStartedFragmented = TRUE; } else{ VolData.ContiguousFileMovesAttempted[VolData.Pass]++; bFileStartedFragmented = FALSE; } // Try to move the file - Don't use the physical cluster count, use the virtual. if (!MoveAPieceOfAFile(Vcn, Lcn, VirtualFileClusters)){ __leave; } // capture the old starting lcn so that we can // detect if it moved... // 'cause sometimes it moves, but not to where // we tell it to. Bummer. oldStartingLcn = pExtents->StartingLcn; // Get the file's new extent list if (!GetExtentList(DEFAULT_STREAMS, NULL)){ EH(FALSE); __leave; } // Reset the file, stream and extent pointers because VolData.pExtentList might // have been realloced in GetExtentList() pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList; pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER)); // Find the stream we were working on before. // Loop through until we hit the one we were on, each time // bumping the stream header to the next stream. for(j=0; jExtentCount * sizeof(EXTENT_LIST)); } // Make sure we didn't go off into never-never-land. if (j >= pFileExtentHeader->NumberOfStreams){ EH(FALSE); __leave; } // We have to get a pointer to the extents again. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER)); // Confirm that the begining of the stream moved. // If not the move failed, try the next algorithm. if(pExtents->StartingLcn == oldStartingLcn) { Message(TEXT("ERROR - MoveFile - Stream didn't move. Go to next file."), -1, NULL); //Since we had a problem, flush the volume. Message(TEXT("MoveFile - Flushing Volume"), -1, NULL); EH(FlushFileBuffers(VolData.hVolume)); VolData.VolumeBufferFlushes[VolData.Pass]++; #ifdef DFRGFAT VolData.Status = NEXT_FILE; #endif __leave; } Lcn += VirtualFileClusters; // Move the stream header to the next stream. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount * sizeof(EXTENT_LIST)); } UpdatePostMoveStats(pFileExtentHeader, llBeforeExtents); bReturnValue = TRUE; // everything went just peachy } __finally { // Validate data and keep track of the percent of the disk that is fragmented. if (VolData.UsedSpace != 0) { VolData.PercentDiskFragged = 100 * VolData.FraggedSpace / VolData.UsedSpace; } else if (VolData.UsedClusters != 0 && VolData.BytesPerCluster != 0) { VolData.PercentDiskFragged = (100 * VolData.FraggedSpace) / (VolData.UsedClusters * VolData.BytesPerCluster); } // Mark the file's new clusters on the disk in the diskview with the appropriate color. if(VolData.bDirectory) { AddExtents(DirectoryColor); } else { if(VolData.bFragmented){ AddExtents(FragmentColor); } else{ AddExtents(UsedSpaceColor); } } #ifdef DFRGFAT // Update the file in its file list. if (!UpdateInFileList()){ EH(FALSE); } else { // Load the volume bitmap. Go to the next pass if there is an error. if(!GetVolumeBitmap()) { Message(TEXT("ERROR - MoveFile - GetVolumeBitmap."), -1, NULL); VolData.Status = NEXT_PASS; bReturnValue = FALSE; } } } if (bReturnValue) { // move succeeded VolData.FilesMoved++; VolData.FilesMovedInLastPass++; // stats for move attempts that succeeded if (bFileStartedFragmented){ VolData.FragmentedFileMovesSucceeded[VolData.Pass]++; } else{ VolData.ContiguousFileMovesSucceeded[VolData.Pass]++; } } else { // stats for move attempts that failed if (bFileStartedFragmented){ VolData.FragmentedFileMovesFailed[VolData.Pass]++; } else{ VolData.ContiguousFileMovesFailed[VolData.Pass]++; } #endif } // Debug // ShowExtentList(); return bReturnValue; } /***************************************************************************************************************** COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc. ROUTINE DESCRIPTION: This routine will partially defragment a file. GLOBALS: VolData RETURN: TRUE = Success FALSE = Failure */ BOOL PartialDefrag( ) { TCHAR cString[500]; UINT i; FILE_EXTENT_HEADER* pFileExtentHeader; STREAM_EXTENT_HEADER* pStreamExtentHeader; EXTENT_LIST* pExtents; LONGLONG llBeforeExtents; LONGLONG VirtualFileClusters; ATTRIBUTE_TYPE_CODE TypeCode = 0; LONGLONG llCurrentFreeCount = 0xffffffffffffffff; LONGLONG llCurrentFreeLcn = 0xffffffffffffffff; LONGLONG llStreamClusterTotal = 0xffffffffffffffff; LONGLONG llRemainingStreamClusters = 0xffffffffffffffff; LONGLONG llCurrentChunkClusters = 0xffffffffffffffff; LONGLONG Vcn = 0; LONGLONG ExcessStreamExtents = 0; LONGLONG StreamClusters = 0; EXTENT_LIST* pFreeExtents = NULL; ULONG ulFreeExtent = 0; BOOL bReturnValue = FALSE; // assume error #ifdef DFRGFAT // Set the status to move the next file. Therefore, unless this variable gets reset // to NEXT_ALGO_STEP within this function, it goes on to the next file by default. VolData.Status = NEXT_FILE; // Validate that we have a file system. if((VolData.FileSystem != FS_NTFS) && (VolData.FileSystem != FS_FAT) && (VolData.FileSystem != FS_FAT32)) { EF_ASSERT(FALSE); } // Debug //ShowExtentList(); #endif //Set up the Extent pointers structure to fill in the extent list in VolData. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList; pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER)); pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER)); __try { // Get a pointer to the free space extents pFreeExtents = (EXTENT_LIST*)GlobalLock((void*)VolData.hFreeExtents); if (pFreeExtents == (EXTENT_LIST*) NULL) { EH(FALSE); __leave; } // check to see if the move gets you anywhere! // find out how many free space extents it takes to hold this file LONGLONG llClusterCount = 0; UINT uFreeSpaceExtentCount = 0; for(i=0; i < VolData.FreeExtents; i++) { llClusterCount += pFreeExtents[i].ClusterCount; uFreeSpaceExtentCount++; if (llClusterCount >= VolData.NumberOfClusters){ break; } } // if it would fragment the file more or if it won't even fit in the existing free space, bag it! if (uFreeSpaceExtentCount >= VolData.NumberOfFragments || llClusterCount < VolData.NumberOfClusters){ Message(TEXT("This PartialDefrag() would fragment the file more - return"), -1, NULL); __leave; } _stprintf(cString, TEXT("Partial Defrag is defragmenting from %I64d fragments down to %d fragments"), VolData.NumberOfFragments, uFreeSpaceExtentCount); Message(cString, -1, NULL); // Mark the file as free space before we move it. After we move it, // the new blocks will be colored in with the correct color. if (!AddExtents(FreeSpaceColor)){ EH(FALSE); __leave; } // Record the number of extents in the stream before we move it. llBeforeExtents = pFileExtentHeader->ExcessExtents; #ifdef ESI_MESSAGE_WINDOW #ifdef DFRGNTFS /* DEBUG stuff... for(i=0; i < VolData.FreeExtents; i++) { // Say where we're moving the file to. wsprintf(cString, TEXT("#%i freespace extnt Lcn %#lx:%#lx"), (UINT)i, (ULONG) pFreeExtents[i].StartingLcn, (ULONG) pFreeExtents[i].ClusterCount); Message(cString, -1, NULL); } */ #endif #endif //Make sure we start using up free space at the beginning of the //freespace extent list ulFreeExtent = 0; // Loop through each stream in the file and move them. for(i=0; iNumberOfStreams; i++) { // If this is not the first stream, then get a handle to the current stream. // We already have a handle to the first stream when this function is called. if(i>0) { #ifdef DFRGNTFS TCHAR StreamName[MAX_PATH]; //Get this stream's streamname. if (!GetStreamNameAndTypeFromNumber(i, StreamName, &TypeCode, NULL)){ EH(FALSE); __leave; } // save a copy of the file name so that we can reset it later PTCHAR strFileName = new TCHAR[VolData.vFileName.GetLength()+1]; if ((strFileName == NULL) || VolData.vFileName.IsEmpty()){ if (strFileName) { delete [] strFileName; strFileName = NULL; } EH(FALSE); __leave; } _tcscpy(strFileName, VolData.vFileName.GetBuffer()); // Append this stream's filename to the end of the file's name. // Note, if there was already another stream's name appended, then // this will simply overwrite it directly. This puts the colon // between the filename and the streamname. VolData.vFileName.AddChar(L':'); // This copies streamname plus terminator to the end. VolData.vFileName += StreamName; // Opens the stream. if (!OpenNtfsFile()){ delete [] strFileName; strFileName = NULL; EH(FALSE); __leave; } // reset it (remove the stream name) so that the next stream will work ok VolData.vFileName = strFileName; delete [] strFileName; strFileName = NULL; #else //If this is not NTFS, then there can't be multiple streams! EH_ASSERT(FALSE); __leave; #endif } // Get the set of free space clusters to use next llCurrentFreeCount = pFreeExtents[ulFreeExtent].ClusterCount; llCurrentFreeLcn = pFreeExtents[ulFreeExtent].StartingLcn; Vcn = 0; VirtualFileClusters = 0; // Make sure we know how many clusters comprise this stream llStreamClusterTotal = pStreamExtentHeader->AllocatedLength / VolData.BytesPerCluster; if ((pStreamExtentHeader->AllocatedLength % VolData.BytesPerCluster) != 0) { llStreamClusterTotal++; } //setup a variable we can count down until the stream is exhausted llRemainingStreamClusters = llStreamClusterTotal; //this loop gets performed until the stream is exhausted while (llRemainingStreamClusters > 0) { // Sleep if paused. while(VolData.EngineState == PAUSED){ Sleep(1000); } // Terminate if told to stop by the controller - this is not an error. if (VolData.EngineState == TERMINATE){ __leave; } //if the remaining clusters are greater than or equal to the current //free space chunk, use up the whole chunk if (llRemainingStreamClusters >= llCurrentFreeCount) { llCurrentChunkClusters = llCurrentFreeCount; } //if the remaining clusters is less than the current //free space chunk, figure out how much of the chunk to use else { llCurrentChunkClusters = llRemainingStreamClusters; llCurrentChunkClusters = (llCurrentChunkClusters +15)&0xfffffffffffffff0; } //debug only // Say where we're moving the file to. wsprintf(cString, TEXT("Moving file %#lx at Vcn %#lx to Lcn %#lx for %#lx"), (ULONG)VolData.FileRecordNumber, (ULONG)Vcn, (ULONG)llCurrentFreeLcn, (ULONG)llCurrentChunkClusters); Message(cString, -1, NULL); // Try to move the file - Don't use the physical cluster count, use the virtual. if (!MoveAPieceOfAFile(Vcn, llCurrentFreeLcn, llCurrentChunkClusters)){ __leave; } //******************************************************************** // Note: the previous incarnation of this code went to great lengths to // determine if a partial move was done. By experiment this is totally // unnecessary. The "partial move" was always picked up in the call to // moveapieceofafile and we left, skipping to the next file anyway. So // I didn't bother accounting for the partial move case in my rewrite. // jlj 15apr99 //******************************************************************** // Update the Vcn to point after the clusters just moved. Vcn = Vcn + llCurrentChunkClusters; // Get the set of free space clusters to use next // (it's possible we didn't use all of them, so just decrement // until it's exhausted) llCurrentFreeCount = llCurrentFreeCount - llCurrentChunkClusters; // Okay, was it exhausted? if (llCurrentFreeCount <= 0) { // means we ran out of free spac in current chunk; get next one ulFreeExtent++; //if we try to get more than we originally found (as stored in // VolData.FreeExtents) then bail out. if (ulFreeExtent > VolData.FreeExtents) { Message(TEXT("ERROR: PartialDefrag Ran out of Free Exents"), -1, NULL); EH_ASSERT(FALSE); __leave; } // Get the set of free space clusters to use next llCurrentFreeCount = pFreeExtents[ulFreeExtent].ClusterCount; llCurrentFreeLcn = pFreeExtents[ulFreeExtent].StartingLcn; } else { //if space left in current free extent, then //just update the current pointer and continue llCurrentFreeLcn = llCurrentFreeLcn + llCurrentChunkClusters; } //okay, it's all done, just countdown the remaining clusters and continue llRemainingStreamClusters = llRemainingStreamClusters - llCurrentChunkClusters; } // come here when done with the current stream //get the current extent list EF(GetExtentList(DEFAULT_STREAMS, NULL)); //Set up the Extent pointers structure to fill in the extent list in VolData. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList; pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER)); pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER)); // Move the stream header to the next stream. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount * sizeof(EXTENT_LIST)); } // come here when all the streams are exhausted (i.e. all done with file) //when we're all done with the file, do the stats UpdatePostMoveStats(pFileExtentHeader, llBeforeExtents); //and consider we finished with no errors bReturnValue = TRUE; } __finally { //come here when all done with the file processing or if any //error occurred. Any "clean up the partial move" is accounted for //here. //get the current extent list if (GetExtentList(DEFAULT_STREAMS, NULL)){ // Validate data and keep track of the percent of the disk that is fragmented. if (VolData.UsedSpace != 0) { VolData.PercentDiskFragged = 100 * VolData.FraggedSpace / VolData.UsedSpace; } else if (VolData.UsedClusters != 0 && VolData.BytesPerCluster != 0) { VolData.PercentDiskFragged = (100 * VolData.FraggedSpace) / (VolData.UsedClusters * VolData.BytesPerCluster); } // Mark the file's new clusters on the disk in the diskview with the appropriate color. if(VolData.bDirectory) { AddExtents(DirectoryColor); } else { if(VolData.bFragmented) { AddExtents(FragmentColor); } else { AddExtents(UsedSpaceColor); } } //Update the file's spot in the file list. if (!UpdateInFileList()){ bReturnValue = FALSE; EH(FALSE); } else { //0.0E00 Load the volume bitmap. Go to the next pass if there is an error. if(!GetVolumeBitmap()) { Message(TEXT("ERROR - PartialDefrag - GetVolumeBitmap()"), -1, NULL); #ifdef DFRGFAT VolData.Status = NEXT_PASS; #endif bReturnValue = FALSE; } } } // do the stats for this if we figure we moved it in its entirety if (bReturnValue) { VolData.FilesMoved++; VolData.FilesMovedInLastPass++; } } return bReturnValue; } /***************************************************************************************************************** COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc. ROUTINE DESCRIPTION: This routine moves a section (or all) of a file to a new location on the disk. GLOBALS: VolData RETURN: TRUE = Success FALSE = Failure */ BOOL MoveAPieceOfAFile( IN LONGLONG FileVcn, IN LONGLONG FreeLcn, IN LONGLONG VirtualClustersToMove ) { MOVE_FILE_DATA MoveData; ULONG BytesReturned; LONGLONG ntStatus; DWORD dwError = ERROR_SUCCESS; // Three tries for (int i=0; i<3; i++) { // Initialize the call to the hook to move this file. MoveData.FileHandle = VolData.hFile; MoveData.StartingVcn.QuadPart = FileVcn; MoveData.StartingLcn.QuadPart = FreeLcn; MoveData.ClusterCount = (ULONG)VirtualClustersToMove; // Call the MoveFile hook. if(ESDeviceIoControl(VolData.hVolume, FSCTL_MOVE_FILE, &MoveData, sizeof(MOVE_FILE_DATA), &ntStatus, sizeof(LONGLONG), &BytesReturned, NULL)) { #ifdef DFRGNTFS VolData.Status = ERROR_SUCCESS; #elif DFRGFAT VolData.Status = NEXT_FILE; #endif return TRUE; } // Get the error and display. dwError = GetLastError(); #ifdef DFRGNTFS VolData.Status = dwError; #endif Message(TEXT("MoveAPieceOfAFile - GetLastError = "), dwError, NULL); Message(TEXT("MoveAPieceOfAFile - ntStatus = "), (DWORD)ntStatus, NULL); Trace(log, " FSCTL_MOVE_FILE failed. " "File FRN:%I64d StartingLcn:%I64d ClusterCount:%I64d. " "Free-space StartingLcn:%I64d ClusterCount:%I64d. Error %lu", VolData.FileRecordNumber, VolData.StartingLcn, VolData.NumberOfClusters, VolData.FoundLcn, VolData.FoundLen, dwError); // This is what is returned if the hook does not have directory move enabled. if (dwError == ERROR_INVALID_PARAMETER) { // Go to the next file. #ifdef DFRGFAT VolData.Status = NEXT_FILE; #endif return FALSE; } // We got an error-retry sleep and try again. else if (dwError == ERROR_RETRY) { Sleep(300); } // Some other error, break from the loop and handle below. else { break; } } // In all other error cases flush the volume. Message(TEXT("Flushing Volume"), -1, NULL); VolData.VolumeBufferFlushes[VolData.Pass]++; EH(FlushFileBuffers(VolData.hVolume)); #ifdef DFRGNTFS VolData.Status = dwError; #elif DFRGFAT // If an error occured go to the next algorithm step. VolData.Status = NEXT_ALGO_STEP; #endif return FALSE; } /***************************************************************************************************************** COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc. ROUTINE DESCRIPTION: This routine can be used during debugging to display the extent list for a file. GLOBALS: IN VolData.pExtentList - The extent list for the file to show. IN VolData.NumberOfFragments - The number of extents in the extent list. RETURN: TRUE = Success FALSE = Failure */ VOID ShowExtentList( ) { EXTENT_LIST* pExtentList; LONGLONG Extent; TCHAR cString[300]; pExtentList = (EXTENT_LIST*)(VolData.pExtentList + sizeof(STREAM_EXTENT_HEADER) + sizeof(FILE_EXTENT_HEADER) ); for(Extent = 0; Extent < VolData.NumberOfFragments; Extent ++) { wsprintf(cString, TEXT("StartingLcn = 0x%lx ClusterCount = 0x%lx"), (ULONG)((pExtentList[Extent].StartingLcn != 0xFFFFFFFFFFFFFFFF) ? pExtentList[Extent].StartingLcn : 0), (ULONG)pExtentList[Extent].ClusterCount); Message(cString, -1, NULL); } } /**/ static void UpdatePostMoveStats(FILE_EXTENT_HEADER* pFileExtentHeader, LONGLONG llBeforeExtents) { if (VolData.bDirectory) { // Update the number of excees directory fragments. VolData.NumExcessDirFrags -= llBeforeExtents - pFileExtentHeader->ExcessExtents; // Directory was defragmented. if(llBeforeExtents && !pFileExtentHeader->ExcessExtents) { VolData.NumFraggedDirs--; VolData.FraggedSpace -= VolData.NumberOfRealClusters * VolData.BytesPerCluster; Message(TEXT("The directory has been successfully defragmented."), -1, NULL); EH(LogEvent(MSG_ENGINE_DEFRAGMENT, ESICompressFilePath(VolData.cFileName))); } // Move of contiguous directory to consolidate free space. // This includes compressed directories with contiguous extents. else if(llBeforeExtents == pFileExtentHeader->ExcessExtents) { Message(TEXT("The directory has been successfully moved to consolidate free space."), -1, NULL); EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName))); } // The directory was contiguous and was fragmented to consolidate free space. else if(!llBeforeExtents && pFileExtentHeader->ExcessExtents) { VolData.NumFraggedDirs++; VolData.FraggedSpace += VolData.NumberOfRealClusters * VolData.BytesPerCluster; Message(TEXT("The directory has been temporarily fragmented in order to consolidate free space."), -1, NULL); EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName))); } } // Update the disk fragmentation and file fragmentation stats. else { // Update the number of excees file fragments. VolData.NumExcessFrags -= llBeforeExtents - pFileExtentHeader->ExcessExtents; // File was defragmented file. if(llBeforeExtents && !pFileExtentHeader->ExcessExtents) { VolData.NumFraggedFiles--; VolData.FraggedSpace -= VolData.NumberOfRealClusters * VolData.BytesPerCluster; Message(TEXT("The file has been successfully defragmented."), -1, NULL); EH(LogEvent(MSG_ENGINE_DEFRAGMENT, ESICompressFilePath(VolData.cFileName))); } // Move of contiguous file to consolidate free space. // This includes compressed files with contiguous extents. else if(llBeforeExtents == pFileExtentHeader->ExcessExtents) { Message(TEXT("The file has been successfully moved to consolidate free space."), -1, NULL); EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName))); } // The directory was contiguous and was fragmented to consolidate free space. else if(!llBeforeExtents && pFileExtentHeader->ExcessExtents) { VolData.NumFraggedFiles++; VolData.FraggedSpace += VolData.NumberOfRealClusters * VolData.BytesPerCluster; Message(TEXT("The file has been temporarily fragmented in order to consolidate free space."), -1, NULL); EH(LogEvent(MSG_ENGINE_FREE_SPACE, ESICompressFilePath(VolData.cFileName))); } // Validate data - keep track of the average number of fragments per file. if((VolData.NumFraggedFiles != 0) && (VolData.CurrentFile != 0)) { VolData.AveFragsPerFile = (pFileExtentHeader->ExcessExtents + VolData.CurrentFile) * 100 / VolData.CurrentFile; } else { VolData.AveFragsPerFile = 100; } } }