e62c8608a4fd1f8c493272c71a375dd1949cd744
1 #include "Defragment.h"
4 // Ahh yes I ripped this from my old Findupes project :)
5 // Fits a path name, composed of a path (i.e. "c:\blah\blah\cha\cha") and a filename ("stuff.txt")
6 // and fits it to a given length. If it has to truncate it will first truncate from the path,
7 // substituting in periods. So you might end up with something like:
8 // C:\Program Files\Micro...\Register.exe
9 int FitName (wchar_t *destination
, const wchar_t *path
, const wchar_t *filename
, uint32 totalWidth
)
14 uint32 len4fn
=0; /* number of chars remaining for filename after path is applied */
15 uint32 len4path
=0; /* number of chars for path before filename is applied */
16 wchar_t fmtStrPath
[20]=L
"";
17 wchar_t fmtStrFile
[20]=L
"";
18 wchar_t fmtString
[40]=L
"";
21 assert (destination != NULL);
22 assert (path != NULL);
23 assert (filename != NULL);
24 assert (totalWidth != 0);
27 pathLen
= wcslen(path
);
28 fnLen
= wcslen(filename
);
29 if (!(totalWidth
% 2))
30 halfTotLen
=totalWidth
/ 2;
32 halfTotLen
=(totalWidth
-1) / 2; /* -1 because otherwise (halfTotLen*2) ==
33 (totalWidth+1) which wouldn't be good */
35 /* determine how much width the path and filename each get */
36 if ( (pathLen
>= halfTotLen
) && (fnLen
< halfTotLen
) )
39 len4path
= (totalWidth
- len4fn
);
42 if ( (pathLen
< halfTotLen
) && (fnLen
< halfTotLen
) )
48 if ( (pathLen
>= halfTotLen
) && (fnLen
>= halfTotLen
) )
51 len4path
= halfTotLen
;
54 if ( (pathLen
< halfTotLen
) && (fnLen
>= halfTotLen
) )
57 len4fn
= (totalWidth
- len4path
);
60 if halfTotLen was adjusted above to avoid a rounding error, give the
61 extra wchar_t to the filename
63 if (halfTotLen
< (totalWidth
/2)) len4path
++;
65 if (pathLen
> len4path
) swprintf (fmtStrPath
, L
"%%.%ds...\\", len4path
-4);
67 swprintf (fmtStrPath
, L
"%%s");
69 if (fnLen
> len4fn
) swprintf (fmtStrFile
, L
"%%.%ds...", len4fn
-3);
71 swprintf (fmtStrFile
, L
"%%s");
73 wcscpy (fmtString
, fmtStrPath
);
74 wcscat (fmtString
, fmtStrFile
);
75 /*swprintf (fmtString, L"%s%s", fmtStrPath, fmtStrFile);*/
76 swprintf (destination
, fmtString
, path
,filename
);
81 Defragment::Defragment (wstring Name
, DefragType DefragMethod
)
83 Method
= DefragMethod
;
91 LastBMPUpdate
= GetTickCount ();
93 SetStatusString (L
"Opening volume " + Name
);
94 if (!Volume
.Open (Name
))
96 SetStatusString (L
"Error opening volume " + Name
);
99 StatusPercent
= 100.0f
;
106 Defragment::~Defragment ()
111 while (!IsDoneYet() && !HasError())
113 SetStatusString (L
"Waiting for thread to stop ...");
123 void Defragment::SetStatusString (wstring NewStatus
)
126 StatusString
= NewStatus
;
133 wstring
Defragment::GetStatusString (void)
138 ReturnVal
= StatusString
;
145 double Defragment::GetStatusPercent (void)
147 return (StatusPercent
);
151 bool Defragment::IsDoneYet (void)
157 void Defragment::Start (void)
161 uint64 TotalClusters
;
162 uint64 ClustersProgress
;
163 wchar_t PrintName
[80];
169 // First thing: build a file list.
170 SetStatusString (L
"Getting volume bitmap");
171 if (!Volume
.GetBitmap())
173 SetStatusString (L
"Could not get volume " + DriveName
+ L
" bitmap");
178 LastBMPUpdate
= GetTickCount ();
183 SetStatusString (L
"Obtaining volume geometry");
184 if (!Volume
.ObtainInfo ())
186 SetStatusString (L
"Could not obtain volume " + DriveName
+ L
" geometry");
194 SetStatusString (L
"Building file database for volume " + DriveName
);
195 if (!Volume
.BuildFileList (PleaseStop
, StatusPercent
))
197 SetStatusString (L
"Could not build file database for volume " + DriveName
);
205 SetStatusString (L
"Analyzing database for " + DriveName
);
207 for (i
= 0; i
< Volume
.GetDBFileCount(); i
++)
209 TotalClusters
+= Volume
.GetDBFile(i
).Clusters
;
213 ClustersProgress
= 0;
215 // Find first free LCN for speedier searches ...
216 Volume
.FindFreeRange (0, 1, FirstFreeLCN
);
222 if (Method
== DefragAnalyze
)
226 Report
.RootPath
= Volume
.GetRootPath ();
228 Report
.FraggedFiles
.clear ();
229 Report
.UnfraggedFiles
.clear ();
230 Report
.UnmovableFiles
.clear ();
232 Report
.FilesCount
= Volume
.GetDBFileCount () - Volume
.GetDBDirCount ();
233 Report
.DirsCount
= Volume
.GetDBDirCount ();
234 Report
.DiskSizeBytes
= Volume
.GetVolumeInfo().TotalBytes
;
236 Report
.FilesSizeClusters
= 0;
237 Report
.FilesSlackBytes
= 0;
238 Report
.FilesSizeBytes
= 0;
239 Report
.FilesFragments
= 0;
241 for (j
= 0; j
< Volume
.GetDBFileCount(); j
++)
245 Info
= Volume
.GetDBFile (j
);
247 Report
.FilesFragments
+= max ((size_t)1, Info
.Fragments
.size()); // add 1 fragment even for 0 bytes/0 cluster files
249 if (Info
.Attributes
.Process
== 0)
252 SetStatusString (Volume
.GetDBDir (Info
.DirIndice
) + Info
.Name
);
254 Report
.FilesSizeClusters
+= Info
.Clusters
;
255 Report
.FilesSizeBytes
+= Info
.Size
;
257 if (Info
.Attributes
.Unmovable
== 1)
258 Report
.UnmovableFiles
.push_back (j
);
260 if (Info
.Fragments
.size() > 1)
261 Report
.FraggedFiles
.push_back (j
);
263 Report
.UnfraggedFiles
.push_back (j
);
265 StatusPercent
= ((double)j
/ (double)Report
.FilesCount
) * 100.0f
;
268 Report
.FilesSizeOnDisk
= Report
.FilesSizeClusters
* (uint64
)Volume
.GetVolumeInfo().ClusterSize
;
269 Report
.FilesSlackBytes
= Report
.FilesSizeOnDisk
- Report
.FilesSizeBytes
;
270 Report
.AverageFragments
= (double)Report
.FilesFragments
/ (double)Report
.FilesCount
;
271 Report
.PercentFragged
= 100.0f
* ((double)(signed)Report
.FraggedFiles
.size() / (double)(signed)Report
.FilesCount
);
274 Percent
= (10000 * Report
.FilesSlackBytes
) / Report
.FilesSizeOnDisk
;
275 Report
.PercentSlack
= (double)(signed)Percent
/ 100.0f
;
278 // Go through all the files and ... defragment them!
279 for (i
= 0; i
< Volume
.GetDBFileCount(); i
++)
284 uint64 PreviousClusters
;
286 // What? They want us to pause? Oh ok.
289 SetStatusString (L
"Paused");
292 while (PleasePause
== false)
302 SetStatusString (L
"Stopping");
307 Info
= Volume
.GetDBFile (i
);
309 PreviousClusters
= ClustersProgress
;
310 ClustersProgress
+= Info
.Clusters
;
312 if (Info
.Attributes
.Process
== 0)
316 SetStatusString (Volume
.GetDBDir (Info
.DirIndice
) + Info
.Name
);
319 FitName (PrintName
, Volume
.GetDBDir (Info
.DirIndice
).c_str(), Info
.Name
.c_str(), Width
);
320 SetStatusString (PrintName
);
323 // Calculate percentage complete
324 StatusPercent
= 100.0f
* double((double)PreviousClusters
/ (double)TotalClusters
);
326 // Can't defrag directories yet
327 if (Info
.Attributes
.Directory
== 1)
330 // Can't defrag 0 byte files :)
331 if (Info
.Fragments
.size() == 0)
334 // If doing fast defrag, skip non-fragmented files
335 // Note: This assumes that the extents stored in Info.Fragments
336 // are consolidated. I.e. we assume it is NOT the case that
337 // two extents account for a sequential range of (non-
338 // fragmented) clusters.
339 if (Info
.Fragments
.size() == 1 && Method
== DefragFast
)
342 // Otherwise, defrag0rize it!
343 int Retry
= 3; // retry a few times
346 // Find a place that can fit the file
347 Result
= Volume
.FindFreeRange (FirstFreeLCN
, Info
.Clusters
, TargetLCN
);
349 // If yes, try moving it
352 // If we're doing an extensive defrag and the file is already defragmented
353 // and if its new location would be after its current location, don't
355 if (Method
== DefragExtensive
&& Info
.Fragments
.size() == 1 &&
356 TargetLCN
> Info
.Fragments
[0].StartLCN
)
362 if (Volume
.MoveFileDumb (i
, TargetLCN
))
364 Retry
= 1; // yay, all done with this file.
365 Volume
.FindFreeRange (0, 1, FirstFreeLCN
);
370 // New: Only update bitmap if it's older than 15 seconds
371 if ((GetTickCount() - LastBMPUpdate
) < 15000)
374 if (!Result
|| Retry
!= 1)
375 { // hmm. Wait for a moment, then update the drive bitmap
376 //SetStatusString (L"(Reobtaining volume " + DriveName + L" bitmap)");
380 SetStatusString (GetStatusString() + wstring (L
" ."));
383 if (Volume
.GetBitmap ())
385 LastBMPUpdate
= GetTickCount ();
388 SetStatusString (Volume
.GetDBDir (Info
.DirIndice
) + Info
.Name
);
390 SetStatusString (PrintName
);
392 Volume
.FindFreeRange (0, 1, FirstFreeLCN
);
396 SetStatusString (L
"Could not re-obtain volume " + DriveName
+ L
" bitmap");
411 OldStatus
= GetStatusString ();
412 StatusPercent
= 99.999999f
;
413 SetStatusString (L
"Closing volume " + DriveName
);
415 StatusPercent
= 100.0f
;
417 // If there was an error then the wstring has already been set
419 SetStatusString (OldStatus
);
422 SetStatusString (L
"Volume " + DriveName
+ L
" defragmentation was stopped");
424 SetStatusString (L
"Finished defragmenting " + DriveName
);
432 void Defragment::TogglePause (void)
435 SetStatusString (L
"Pausing ...");
443 void Defragment::Stop (void)
446 SetStatusString (L
"Stopping ...");
454 bool Defragment::HasError (void)