--- /dev/null
+#include "Defragment.h"\r
+\r
+\r
+// Ahh yes I ripped this from my old Findupes project :)\r
+// Fits a path name, composed of a path (i.e. "c:\blah\blah\cha\cha") and a filename ("stuff.txt")\r
+// and fits it to a given length. If it has to truncate it will first truncate from the path,\r
+// substituting in periods. So you might end up with something like:\r
+// C:\Program Files\Micro...\Register.exe\r
+int FitName (wchar_t *destination, const wchar_t *path, const wchar_t *filename, uint32 totalWidth)\r
+{\r
+ uint32 pathLen=0;\r
+ uint32 fnLen=0;\r
+ uint32 halfTotLen=0;\r
+ uint32 len4fn=0; /* number of chars remaining for filename after path is applied */\r
+ uint32 len4path=0; /* number of chars for path before filename is applied */\r
+ wchar_t fmtStrPath[20]=L"";\r
+ wchar_t fmtStrFile[20]=L"";\r
+ wchar_t fmtString[40]=L"";\r
+\r
+ /*\r
+ assert (destination != NULL);\r
+ assert (path != NULL);\r
+ assert (filename != NULL);\r
+ assert (totalWidth != 0);\r
+ */\r
+\r
+ pathLen = wcslen(path);\r
+ fnLen = wcslen(filename);\r
+ if (!(totalWidth % 2))\r
+ halfTotLen=totalWidth / 2;\r
+ else\r
+ halfTotLen=(totalWidth-1) / 2; /* -1 because otherwise (halfTotLen*2) ==\r
+(totalWidth+1) which wouldn't be good */\r
+\r
+ /* determine how much width the path and filename each get */\r
+ if ( (pathLen >= halfTotLen) && (fnLen < halfTotLen) )\r
+ {\r
+ len4fn = fnLen;\r
+ len4path = (totalWidth - len4fn);\r
+ }\r
+\r
+ if ( (pathLen < halfTotLen) && (fnLen < halfTotLen) )\r
+ {\r
+ len4fn = fnLen;\r
+ len4path = pathLen;\r
+ }\r
+\r
+ if ( (pathLen >= halfTotLen) && (fnLen >= halfTotLen) )\r
+ {\r
+ len4fn = halfTotLen;\r
+ len4path = halfTotLen;\r
+ }\r
+\r
+ if ( (pathLen < halfTotLen) && (fnLen >= halfTotLen) )\r
+ {\r
+ len4path = pathLen;\r
+ len4fn = (totalWidth - len4path);\r
+ }\r
+ /*\r
+ if halfTotLen was adjusted above to avoid a rounding error, give the\r
+ extra wchar_t to the filename\r
+ */\r
+ if (halfTotLen < (totalWidth/2)) len4path++;\r
+\r
+ if (pathLen > len4path) swprintf (fmtStrPath, L"%%.%ds...\\", len4path-4);\r
+ else\r
+ swprintf (fmtStrPath, L"%%s");\r
+\r
+ if (fnLen > len4fn) swprintf (fmtStrFile, L"%%.%ds...", len4fn-3);\r
+ else\r
+ swprintf (fmtStrFile, L"%%s");\r
+\r
+ wcscpy (fmtString, fmtStrPath);\r
+ wcscat (fmtString, fmtStrFile);\r
+ /*swprintf (fmtString, L"%s%s", fmtStrPath, fmtStrFile);*/\r
+ swprintf (destination, fmtString, path,filename);\r
+\r
+ return (1);\r
+}\r
+\r
+Defragment::Defragment (wstring Name, DefragType DefragMethod)\r
+{\r
+ Method = DefragMethod;\r
+ DoLimitLength = true;\r
+ Error = false;\r
+ Done = false;\r
+ PleaseStop = false;\r
+ PleasePause = false;\r
+ DriveName = Name;\r
+ StatusPercent = 0.0f;\r
+ LastBMPUpdate = GetTickCount ();\r
+\r
+ SetStatusString (L"Opening volume " + Name);\r
+ if (!Volume.Open (Name))\r
+ {\r
+ SetStatusString (L"Error opening volume " + Name);\r
+ Error = true;\r
+ Done = true;\r
+ StatusPercent = 100.0f;\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+\r
+Defragment::~Defragment ()\r
+{\r
+ if (!IsDoneYet ())\r
+ {\r
+ Stop ();\r
+ while (!IsDoneYet() && !HasError())\r
+ {\r
+ SetStatusString (L"Waiting for thread to stop ...");\r
+ Sleep (150);\r
+ }\r
+ }\r
+\r
+ Volume.Close ();\r
+ return;\r
+}\r
+\r
+\r
+void Defragment::SetStatusString (wstring NewStatus)\r
+{\r
+ Lock ();\r
+ StatusString = NewStatus;\r
+ Unlock ();\r
+\r
+ return;\r
+}\r
+\r
+\r
+wstring Defragment::GetStatusString (void)\r
+{\r
+ wstring ReturnVal;\r
+\r
+ Lock ();\r
+ ReturnVal = StatusString;\r
+ Unlock ();\r
+\r
+ return (ReturnVal);\r
+}\r
+\r
+\r
+double Defragment::GetStatusPercent (void)\r
+{\r
+ return (StatusPercent);\r
+}\r
+\r
+\r
+bool Defragment::IsDoneYet (void)\r
+{\r
+ return (Done);\r
+}\r
+\r
+\r
+void Defragment::Start (void)\r
+{\r
+ uint32 i;\r
+ uint64 FirstFreeLCN;\r
+ uint64 TotalClusters;\r
+ uint64 ClustersProgress;\r
+ wchar_t PrintName[80];\r
+ int Width = 70;\r
+\r
+ if (Error)\r
+ goto DoneDefrag;\r
+\r
+ // First thing: build a file list.\r
+ SetStatusString (L"Getting volume bitmap");\r
+ if (!Volume.GetBitmap())\r
+ {\r
+ SetStatusString (L"Could not get volume " + DriveName + L" bitmap");\r
+ Error = true;\r
+ goto DoneDefrag;\r
+ }\r
+\r
+ LastBMPUpdate = GetTickCount ();\r
+\r
+ if (PleaseStop)\r
+ goto DoneDefrag;\r
+\r
+ SetStatusString (L"Obtaining volume geometry");\r
+ if (!Volume.ObtainInfo ())\r
+ {\r
+ SetStatusString (L"Could not obtain volume " + DriveName + L" geometry");\r
+ Error = true;\r
+ goto DoneDefrag;\r
+ }\r
+\r
+ if (PleaseStop)\r
+ goto DoneDefrag;\r
+\r
+ SetStatusString (L"Building file database for volume " + DriveName);\r
+ if (!Volume.BuildFileList (PleaseStop, StatusPercent))\r
+ {\r
+ SetStatusString (L"Could not build file database for volume " + DriveName);\r
+ Error = true;\r
+ goto DoneDefrag;\r
+ }\r
+\r
+ if (PleaseStop)\r
+ goto DoneDefrag;\r
+\r
+ SetStatusString (L"Analyzing database for " + DriveName);\r
+ TotalClusters = 0;\r
+ for (i = 0; i < Volume.GetDBFileCount(); i++)\r
+ {\r
+ TotalClusters += Volume.GetDBFile(i).Clusters;\r
+ }\r
+\r
+ // Defragment!\r
+ ClustersProgress = 0;\r
+\r
+ // Find first free LCN for speedier searches ...\r
+ Volume.FindFreeRange (0, 1, FirstFreeLCN);\r
+\r
+ if (PleaseStop)\r
+ goto DoneDefrag;\r
+\r
+ // Analyze?\r
+ if (Method == DefragAnalyze)\r
+ {\r
+ uint32 j;\r
+\r
+ Report.RootPath = Volume.GetRootPath ();\r
+\r
+ Report.FraggedFiles.clear ();\r
+ Report.UnfraggedFiles.clear ();\r
+ Report.UnmovableFiles.clear ();\r
+\r
+ Report.FilesCount = Volume.GetDBFileCount () - Volume.GetDBDirCount ();\r
+ Report.DirsCount = Volume.GetDBDirCount ();\r
+ Report.DiskSizeBytes = Volume.GetVolumeInfo().TotalBytes;\r
+\r
+ Report.FilesSizeClusters = 0;\r
+ Report.FilesSlackBytes = 0;\r
+ Report.FilesSizeBytes = 0;\r
+ Report.FilesFragments = 0;\r
+\r
+ for (j = 0; j < Volume.GetDBFileCount(); j++)\r
+ {\r
+ FileInfo Info;\r
+\r
+ Info = Volume.GetDBFile (j);\r
+\r
+ Report.FilesFragments += max ((size_t)1, Info.Fragments.size()); // add 1 fragment even for 0 bytes/0 cluster files\r
+\r
+ if (Info.Attributes.Process == 0)\r
+ continue;\r
+\r
+ SetStatusString (Volume.GetDBDir (Info.DirIndice) + Info.Name);\r
+\r
+ Report.FilesSizeClusters += Info.Clusters;\r
+ Report.FilesSizeBytes += Info.Size;\r
+\r
+ if (Info.Attributes.Unmovable == 1)\r
+ Report.UnmovableFiles.push_back (j);\r
+\r
+ if (Info.Fragments.size() > 1)\r
+ Report.FraggedFiles.push_back (j);\r
+ else\r
+ Report.UnfraggedFiles.push_back (j);\r
+\r
+ StatusPercent = ((double)j / (double)Report.FilesCount) * 100.0f;\r
+ }\r
+\r
+ Report.FilesSizeOnDisk = Report.FilesSizeClusters * (uint64)Volume.GetVolumeInfo().ClusterSize;\r
+ Report.FilesSlackBytes = Report.FilesSizeOnDisk - Report.FilesSizeBytes;\r
+ Report.AverageFragments = (double)Report.FilesFragments / (double)Report.FilesCount;\r
+ Report.PercentFragged = 100.0f * ((double)(signed)Report.FraggedFiles.size() / (double)(signed)Report.FilesCount);\r
+\r
+ uint64 Percent;\r
+ Percent = (10000 * Report.FilesSlackBytes) / Report.FilesSizeOnDisk;\r
+ Report.PercentSlack = (double)(signed)Percent / 100.0f;\r
+ }\r
+ else\r
+ // Go through all the files and ... defragment them!\r
+ for (i = 0; i < Volume.GetDBFileCount(); i++)\r
+ {\r
+ FileInfo Info;\r
+ bool Result;\r
+ uint64 TargetLCN;\r
+ uint64 PreviousClusters;\r
+\r
+ // What? They want us to pause? Oh ok.\r
+ if (PleasePause)\r
+ {\r
+ SetStatusString (L"Paused");\r
+ PleasePause = false;\r
+\r
+ while (PleasePause == false)\r
+ {\r
+ Sleep (50);\r
+ }\r
+\r
+ PleasePause = false;\r
+ }\r
+\r
+ if (PleaseStop)\r
+ {\r
+ SetStatusString (L"Stopping");\r
+ break;\r
+ }\r
+\r
+ //\r
+ Info = Volume.GetDBFile (i);\r
+\r
+ PreviousClusters = ClustersProgress;\r
+ ClustersProgress += Info.Clusters;\r
+\r
+ if (Info.Attributes.Process == 0)\r
+ continue;\r
+\r
+ if (!DoLimitLength)\r
+ SetStatusString (Volume.GetDBDir (Info.DirIndice) + Info.Name);\r
+ else\r
+ {\r
+ FitName (PrintName, Volume.GetDBDir (Info.DirIndice).c_str(), Info.Name.c_str(), Width);\r
+ SetStatusString (PrintName);\r
+ }\r
+\r
+ // Calculate percentage complete\r
+ StatusPercent = 100.0f * double((double)PreviousClusters / (double)TotalClusters);\r
+\r
+ // Can't defrag directories yet\r
+ if (Info.Attributes.Directory == 1)\r
+ continue;\r
+\r
+ // Can't defrag 0 byte files :)\r
+ if (Info.Fragments.size() == 0)\r
+ continue;\r
+\r
+ // If doing fast defrag, skip non-fragmented files\r
+ // Note: This assumes that the extents stored in Info.Fragments\r
+ // are consolidated. I.e. we assume it is NOT the case that\r
+ // two extents account for a sequential range of (non-\r
+ // fragmented) clusters.\r
+ if (Info.Fragments.size() == 1 && Method == DefragFast)\r
+ continue;\r
+\r
+ // Otherwise, defrag0rize it!\r
+ int Retry = 3; // retry a few times\r
+ while (Retry > 0)\r
+ {\r
+ // Find a place that can fit the file\r
+ Result = Volume.FindFreeRange (FirstFreeLCN, Info.Clusters, TargetLCN);\r
+\r
+ // If yes, try moving it\r
+ if (Result)\r
+ {\r
+ // If we're doing an extensive defrag and the file is already defragmented\r
+ // and if its new location would be after its current location, don't\r
+ // move it.\r
+ if (Method == DefragExtensive && Info.Fragments.size() == 1 &&\r
+ TargetLCN > Info.Fragments[0].StartLCN)\r
+ {\r
+ Retry = 1;\r
+ }\r
+ else\r
+ {\r
+ if (Volume.MoveFileDumb (i, TargetLCN))\r
+ {\r
+ Retry = 1; // yay, all done with this file.\r
+ Volume.FindFreeRange (0, 1, FirstFreeLCN);\r
+ }\r
+ }\r
+ }\r
+\r
+ // New: Only update bitmap if it's older than 15 seconds\r
+ if ((GetTickCount() - LastBMPUpdate) < 15000)\r
+ Retry = 1;\r
+ else\r
+ if (!Result || Retry != 1)\r
+ { // hmm. Wait for a moment, then update the drive bitmap\r
+ //SetStatusString (L"(Reobtaining volume " + DriveName + L" bitmap)");\r
+\r
+ if (!DoLimitLength)\r
+ {\r
+ SetStatusString (GetStatusString() + wstring (L" ."));\r
+ }\r
+\r
+ if (Volume.GetBitmap ())\r
+ {\r
+ LastBMPUpdate = GetTickCount ();\r
+\r
+ if (!DoLimitLength)\r
+ SetStatusString (Volume.GetDBDir (Info.DirIndice) + Info.Name);\r
+ else\r
+ SetStatusString (PrintName);\r
+\r
+ Volume.FindFreeRange (0, 1, FirstFreeLCN);\r
+ }\r
+ else\r
+ {\r
+ SetStatusString (L"Could not re-obtain volume " + DriveName + L" bitmap");\r
+ Error = true;\r
+ }\r
+ }\r
+\r
+ Retry--;\r
+ }\r
+\r
+ if (Error == true)\r
+ break;\r
+ }\r
+\r
+DoneDefrag:\r
+ wstring OldStatus;\r
+\r
+ OldStatus = GetStatusString ();\r
+ StatusPercent = 99.999999f;\r
+ SetStatusString (L"Closing volume " + DriveName);\r
+ Volume.Close ();\r
+ StatusPercent = 100.0f;\r
+\r
+ // If there was an error then the wstring has already been set\r
+ if (Error)\r
+ SetStatusString (OldStatus);\r
+ else\r
+ if (PleaseStop)\r
+ SetStatusString (L"Volume " + DriveName + L" defragmentation was stopped");\r
+ else\r
+ SetStatusString (L"Finished defragmenting " + DriveName);\r
+\r
+ Done = true;\r
+\r
+ return;\r
+}\r
+\r
+\r
+void Defragment::TogglePause (void)\r
+{\r
+ Lock ();\r
+ SetStatusString (L"Pausing ...");\r
+ PleasePause = true;\r
+ Unlock ();\r
+\r
+ return;\r
+}\r
+\r
+\r
+void Defragment::Stop (void)\r
+{\r
+ Lock ();\r
+ SetStatusString (L"Stopping ...");\r
+ PleaseStop = true;\r
+ Unlock ();\r
+\r
+ return;\r
+}\r
+\r
+\r
+bool Defragment::HasError (void)\r
+{\r
+ return (Error);\r
+}\r
+\r