1 /*****************************************************************************
5 *****************************************************************************/
9 #include "DriveVolume.h"
10 #include "Defragment.h"
14 bool QuietMode
= false;
15 bool VerboseMode
= false;
18 // Makes sure we're in Windows 2000
19 bool CheckWinVer (void)
21 OSVERSIONINFO OSVersion
;
23 ZeroMemory (&OSVersion
, sizeof (OSVersion
));
24 OSVersion
.dwOSVersionInfoSize
= sizeof (OSVersion
);
25 GetVersionEx (&OSVersion
);
30 // Actually what we do is check that weLL're not on Win31+Win32s and that we're
31 // not in Windows 9x. It's possible that there could be more Windows "platforms"
32 // in the future and there's no sense in claiming incompatibility.
33 if (OSVersion
.dwPlatformId
== VER_PLATFORM_WIN32s
||
34 OSVersion
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
)
39 // Ok weLL're in Windows NT, now make sure we're in 2000
40 if (OSVersion
.dwMajorVersion
< 5)
43 // Kew, we're in at least Windows 2000 ("NT 5.0")
48 wchar_t *AddCommas (wchar_t *Result
, uint64 Number
)
54 wchar_t *StrPosResult
= NULL
;
55 wchar_t *StrPosOrig
= NULL
;
57 // we get the string form of the number, then we count down w/ AddCommas
58 // while copying the string from Temp1 to Result. when AddCommas % 3 == 1,
59 // slap in a commas as well, before the #.
60 swprintf (Temp
, L
"%I64u", Number
);
61 AddCommas
= TempLen
= wcslen (Temp
);
63 StrPosResult
= Result
;
66 if ((AddCommas
% 3) == 0 && AddCommas
!= TempLen
) // avoid stuff like ",345"
72 *StrPosResult
= *StrPosOrig
;
85 void PrintBanner (void)
87 wprintf (L
"%s v%s\n", APPNAME_CLI
, APPVER_STR
);
88 wprintf (L
"%s\n", APPCOPYRIGHT
);
95 void FraggerHelp (void)
97 wprintf (L
"Usage: unfrag drive: [...] <-f | -e>\n");
99 wprintf (L
"drive: : The drive to defrag. Should be two characters long, ie 'c:' or 'd:'.\n");
100 wprintf (L
" Multiple drives may be given, and all will be simultaneously\n");
101 wprintf (L
" defragmented using the same options.\n");
102 wprintf (L
"-f : Do a fast defragmentation. Files that are not fragmented will not be\n");
103 wprintf (L
" moved. Only one pass is made over the file list. Using this option\n");
104 wprintf (L
" may result in not all files being defragmented, depending on\n");
105 wprintf (L
" available disk space.\n");
106 wprintf (L
"-e : Do an extensive defragmention. Files will be moved in an attempt to\n");
107 wprintf (L
" defragment both files and free space.\n");
112 wprintf (L
"NOTE: This program requires Windows 2000, which is not presently running on\n");
113 wprintf (L
"this system.\n");
120 void __cdecl
DefragThread (LPVOID parm
)
124 Defrag
= (Defragment
*) parm
;
132 Defragment
*StartDefragThread (wstring Drive
, DefragType Method
, HANDLE
&Handle
)
134 Defragment
*Defragger
;
135 unsigned long Thread
;
137 Defragger
= new Defragment (Drive
, Method
);
138 //Thread = /*CreateThread*/ _beginthreadex (NULL, 0, DefragThread, Defragger, 0, &ThreadID);
139 Thread
= _beginthread (DefragThread
, 0, Defragger
);
140 Handle
= *((HANDLE
*)&Thread
);
145 // Main Initialization
146 extern "C" int wmain (int argc
, wchar_t **argv
)
148 vector
<wstring
> Drives
;
149 vector
<Defragment
*> Defrags
;
150 DefragType DefragMode
= DefragInvalid
;
154 // Parse command line arguments
155 bool ValidCmdLine
= false;
156 for (int c
= 0; c
< argc
; c
++)
158 if (wcslen(argv
[c
]) == 2 && argv
[c
][1] == L
':')
160 Drives
.push_back (_wcsupr(argv
[c
]));
163 if ((argv
[c
][0] == L
'-' || argv
[c
][0] == L
'/') && wcslen(argv
[c
]) == 2)
165 switch (tolower(argv
[c
][1]))
173 if (DefragMode
!= DefragInvalid
)
175 ValidCmdLine
= false;
178 DefragMode
= DefragFast
;
183 if (DefragMode
!= DefragInvalid
)
185 ValidCmdLine
= false;
188 DefragMode
= DefragExtensive
;
196 if (DefragMode
== DefragInvalid
)
197 ValidCmdLine
= false;
201 wprintf (L
"Invalid command-line options. Use '%s -?' for help.\n", argv
[0]);
205 // Check OS requirements
208 wprintf (L
"Fatal Error: This program requires Windows 2000.\n");
212 for (size_t d
= 0; d
< Drives
.size (); d
++)
215 Defrags
.push_back (StartDefragThread (Drives
[d
], DefragMode
, TossMe
));
218 for (size_t d
= 0; d
< Drives
.size () - 1; d
++)
221 bool Continue
= true;
224 Screen
= GetStdHandle (STD_OUTPUT_HANDLE
);
229 // Get current screen coords
230 CONSOLE_SCREEN_BUFFER_INFO ScreenInfo
;
232 GetConsoleScreenBufferInfo (Screen
, &ScreenInfo
);
234 // Now set back to the beginning
235 ScreenInfo
.dwCursorPosition
.X
= 0;
236 ScreenInfo
.dwCursorPosition
.Y
-= Drives
.size();
237 SetConsoleCursorPosition (Screen
, ScreenInfo
.dwCursorPosition
);
239 for (size_t d
= 0; d
< Drives
.size (); d
++)
241 wprintf (L
"\n%6.2f%% %-70s", Defrags
[d
]->GetStatusPercent(), Defrags
[d
]->GetStatusString().c_str());
244 // Determine if we should keep going
246 for (size_t d
= 0; d
< Drives
.size (); d
++)
248 if (!Defrags
[d
]->IsDoneYet() && !Defrags
[d
]->HasError())
254 // Loop through the drives list
255 for (int d
= 0; d
< Drives
.size(); d
++)
259 Drive
= new DriveVolume
;
261 // First thing: build a file list.
262 wprintf (L
"Opening volume %s ...", Drives
[d
].c_str());
263 if (!Drive
->Open (Drives
[d
]))
265 wprintf (L
"FAILED\n\n");
271 wprintf (L
" Getting drive bitmap ...");
272 if (!Drive
->GetBitmap ())
274 wprintf (L
"FAILED\n\n");
280 wprintf (L
" Obtaining drive geometry ...");
281 if (!Drive
->ObtainInfo ())
283 wprintf (L
"FAILED\n\n");
289 wprintf (L
" Building file database for drive %s ...", Drives
[d
].c_str());
290 if (!Drive
->BuildFileList ())
292 wprintf (L
"FAILED\n\n");
298 wprintf (L
" %u files\n", Drive
->GetDBFileCount ());
301 if (DefragMode
== DefragAnalyze
)
303 uint64 UsedBytes
= 0; // total bytes used, with cluster size considerations
304 uint64 TotalBytes
= 0; // total bytes used
305 uint64 SlackBytes
= 0; // wasted space due to slack
306 uint32 Fragged
= 0; // fragmented files
308 wprintf (L
" Analyzing ...");
312 for (int i
= 0; i
< Drive
->GetDBFileCount(); i
++)
318 Info
= Drive
->GetDBFile (i
);
320 // Compute total used disk space
321 Used
= ((Info
.Size
+ Drive
->GetClusterSize() - 1) / Drive
->GetClusterSize()) * Drive
->GetClusterSize();
322 Slack
= Used
- Info
.Size
;
326 TotalBytes
+= Info
.Size
;
330 wprintf (L
" %s%s, ", Drive
->GetDBDir (Info
.DirIndice
).c_str(), Info
.Name
.c_str());
332 if (Info
.Attributes
.AccessDenied
)
333 wprintf (L
"access was denied\n");
336 if (Info
.Attributes
.Unmovable
== 1)
337 wprintf (L
"unmovable, ");
339 wprintf (L
"%I64u bytes, %I64u bytes on disk, %I64u bytes slack, %u fragments\n",
340 Info
.Size
, Used
, Slack
, Info
.Fragments
.size());
344 if (Info
.Fragments
.size() > 1)
351 // TODO: Make it not look like ass
353 wprintf (L
" Overall Analysis\n");
354 wprintf (L
" ----------------\n");
355 wprintf (L
" %u clusters\n", Drive
->GetClusterCount ());
356 wprintf (L
" %u bytes per cluster\n", Drive
->GetClusterSize());
357 wprintf (L
" %I64u total bytes on drive\n", (uint64
)Drive
->GetClusterCount() * (uint64
)Drive
->GetClusterSize());
359 wprintf (L
" %u files\n", Drive
->GetDBFileCount ());
360 wprintf (L
" %u contiguous files\n", Drive
->GetDBFileCount () - Fragged
);
361 wprintf (L
" %u fragmented files\n", Fragged
);
363 wprintf (L
" %I64u bytes\n", TotalBytes
);
364 wprintf (L
" %I64u bytes on disk\n", UsedBytes
);
365 wprintf (L
" %I64u bytes slack\n", SlackBytes
);
369 if (DefragMode
== DefragFast
|| DefragMode
== DefragExtensive
)
373 wchar_t PrintName
[80];
376 if (DefragMode
== DefragFast
)
377 wprintf (L
" Performing fast file defragmentation ...\n");
379 if (DefragMode
== DefragExtensive
)
380 wprintf (L
" Performing extensive file defragmentation\n");
382 // Find first free LCN for speedier searches ...
383 Drive
->FindFreeRange (0, 1, FirstFreeLCN
);
385 for (i
= 0; i
< Drive
->GetDBFileCount(); i
++)
393 Info
= Drive
->GetDBFile (i
);
395 FitName (PrintName
, Drive
->GetDBDir (Info
.DirIndice
).c_str(), Info
.Name
.c_str(), Width
);
396 wprintf (L
" %6.2f%% %-66s", (float)i
/ (float)Drive
->GetDBFileCount() * 100.0f
, PrintName
);
398 // Can't defrag 0 byte files :)
399 if (Info
.Fragments
.size() == 0)
402 // If doing fast defrag, skip non-fragmented files
403 if (Info
.Fragments
.size() == 1 && DefragMode
== DefragFast
)
406 // Find a place that can fit the file
407 Result
= Drive
->FindFreeRange (FirstFreeLCN
, Info
.Clusters
, TargetLCN
);
409 // If we're doing an extensive defrag and the file is already defragmented
410 // and if its new location would be after its current location, don't
412 if (DefragMode
== DefragExtensive
&& Info
.Fragments
.size() == 1)
414 if (TargetLCN
> Info
.Fragments
[0].StartLCN
)
418 // Otherwise, defrag0rize it!
421 bool Success
= false;
423 if (Drive
->MoveFileDumb (i
, TargetLCN
))
426 { // hmm, look for another area to move it to
427 Result
= Drive
->FindFreeRange (TargetLCN
+ 1, Info
.Clusters
, TargetLCN
);
430 if (Drive
->MoveFileDumb (i
, TargetLCN
))
433 { // Try updating the drive bitmap
434 if (Drive
->GetBitmap ())
436 Result
= Drive
->FindFreeRange (0, Info
.Clusters
, TargetLCN
);
439 if (Drive
->MoveFileDumb (i
, TargetLCN
))
448 wprintf (L
"\n -> failed\n");
450 Drive
->FindFreeRange (0, 1, FirstFreeLCN
);
456 wprintf (L
"Closing volume %s ...", Drives
[d
].c_str());