- Start rosapps rearrange and cleanup process.
[reactos.git] / rosapps / applications / fraginator / Defragment.cpp
diff --git a/rosapps/applications/fraginator/Defragment.cpp b/rosapps/applications/fraginator/Defragment.cpp
new file mode 100644 (file)
index 0000000..e62c860
--- /dev/null
@@ -0,0 +1,458 @@
+#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