0675021501bf5b0c8dc11b9487a54f6aa8cbed23
[reactos.git] / modules / rosapps / applications / fraginator / Unfrag.cpp
1 /*****************************************************************************
2
3 Unfrag
4
5 *****************************************************************************/
6
7
8 #include "Unfrag.h"
9 #include "DriveVolume.h"
10 #include "Defragment.h"
11 #include <process.h>
12
13
14 bool QuietMode = false;
15 bool VerboseMode = false;
16
17
18 // Makes sure we're in Windows 2000
19 bool CheckWinVer (void)
20 {
21 OSVERSIONINFO OSVersion;
22
23 ZeroMemory (&OSVersion, sizeof (OSVersion));
24 OSVersion.dwOSVersionInfoSize = sizeof (OSVersion);
25 GetVersionEx (&OSVersion);
26
27 // Need Windows 2000!
28
29 // Check for NT first
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)
35 {
36 return (false);
37 }
38
39 // Ok weLL're in Windows NT, now make sure we're in 2000
40 if (OSVersion.dwMajorVersion < 5)
41 return (false);
42
43 // Kew, we're in at least Windows 2000 ("NT 5.0")
44 return (true);
45 }
46
47
48 wchar_t *AddCommas (wchar_t *Result, uint64 Number)
49 {
50 wchar_t Temp[128];
51 int TempLen;
52 //wchar_t *p = NULL;
53 int AddCommas = 0;
54 wchar_t *StrPosResult = NULL;
55 wchar_t *StrPosOrig = NULL;
56
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);
62 StrPosOrig = Temp;
63 StrPosResult = Result;
64 while (AddCommas)
65 {
66 if ((AddCommas % 3) == 0 && AddCommas != TempLen) // avoid stuff like ",345"
67 {
68 *StrPosResult = L',';
69 StrPosResult++;
70 }
71
72 *StrPosResult = *StrPosOrig;
73 StrPosResult++;
74 StrPosOrig++;
75
76 *StrPosResult = 0;
77
78 AddCommas--;
79 }
80
81 return (Result);
82 }
83
84
85 void PrintBanner (void)
86 {
87 wprintf (L"%s v%s\n", APPNAME_CLI, APPVER_STR);
88 wprintf (L"%s\n", APPCOPYRIGHT);
89 wprintf (L"\n");
90
91 return;
92 }
93
94
95 void FraggerHelp (void)
96 {
97 wprintf (L"Usage: unfrag drive: [...] <-f | -e>\n");
98 wprintf (L"\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");
108
109 if (!CheckWinVer())
110 {
111 wprintf (L"\n");
112 wprintf (L"NOTE: This program requires Windows 2000, which is not presently running on\n");
113 wprintf (L"this system.\n");
114 }
115
116 return;
117 }
118
119
120 void __cdecl DefragThread (LPVOID parm)
121 {
122 Defragment *Defrag;
123
124 Defrag = (Defragment *) parm;
125 Defrag->Start ();
126
127 _endthread ();
128 return;
129 }
130
131
132 Defragment *StartDefragThread (wstring Drive, DefragType Method, HANDLE &Handle)
133 {
134 Defragment *Defragger;
135 unsigned long Thread;
136
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);
141 return (Defragger);
142 }
143
144 #ifdef _CUI_
145 // Main Initialization
146 extern "C" int wmain (int argc, wchar_t **argv)
147 {
148 vector<wstring> Drives;
149 vector<Defragment *> Defrags;
150 DefragType DefragMode = DefragInvalid;
151
152 PrintBanner ();
153
154 // Parse command line arguments
155 bool ValidCmdLine = false;
156 for (int c = 0; c < argc; c++)
157 {
158 if (wcslen(argv[c]) == 2 && argv[c][1] == L':')
159 {
160 Drives.push_back (_wcsupr(argv[c]));
161 }
162 else
163 if ((argv[c][0] == L'-' || argv[c][0] == L'/') && wcslen(argv[c]) == 2)
164 {
165 switch (tolower(argv[c][1]))
166 {
167 case L'?' :
168 case L'h' :
169 FraggerHelp ();
170 return (0);
171
172 case L'f' :
173 if (DefragMode != DefragInvalid)
174 {
175 ValidCmdLine = false;
176 break;
177 }
178 DefragMode = DefragFast;
179 ValidCmdLine = true;
180 break;
181
182 case L'e' :
183 if (DefragMode != DefragInvalid)
184 {
185 ValidCmdLine = false;
186 break;
187 }
188 DefragMode = DefragExtensive;
189 ValidCmdLine = true;
190 break;
191
192 }
193 }
194 }
195
196 if (DefragMode == DefragInvalid)
197 ValidCmdLine = false;
198
199 if (!ValidCmdLine)
200 {
201 wprintf (L"Invalid command-line options. Use '%s -?' for help.\n", argv[0]);
202 return (0);
203 }
204
205 // Check OS requirements
206 if (!CheckWinVer())
207 {
208 wprintf (L"Fatal Error: This program requires Windows 2000.\n");
209 return (0);
210 }
211
212 for (size_t d = 0; d < Drives.size (); d++)
213 {
214 HANDLE TossMe;
215 Defrags.push_back (StartDefragThread (Drives[d], DefragMode, TossMe));
216 }
217
218 for (size_t d = 0; d < Drives.size () - 1; d++)
219 wprintf (L"\n ");
220
221 bool Continue = true;
222 HANDLE Screen;
223
224 Screen = GetStdHandle (STD_OUTPUT_HANDLE);
225 while (Continue)
226 {
227 Sleep (25);
228
229 // Get current screen coords
230 CONSOLE_SCREEN_BUFFER_INFO ScreenInfo;
231
232 GetConsoleScreenBufferInfo (Screen, &ScreenInfo);
233
234 // Now set back to the beginning
235 ScreenInfo.dwCursorPosition.X = 0;
236 ScreenInfo.dwCursorPosition.Y -= Drives.size();
237 SetConsoleCursorPosition (Screen, ScreenInfo.dwCursorPosition);
238
239 for (size_t d = 0; d < Drives.size (); d++)
240 {
241 wprintf (L"\n%6.2f%% %-70s", Defrags[d]->GetStatusPercent(), Defrags[d]->GetStatusString().c_str());
242 }
243
244 // Determine if we should keep going
245 Continue = false;
246 for (size_t d = 0; d < Drives.size (); d++)
247 {
248 if (!Defrags[d]->IsDoneYet() && !Defrags[d]->HasError())
249 Continue = true;
250 }
251 }
252
253 #if 0
254 // Loop through the drives list
255 for (int d = 0; d < Drives.size(); d++)
256 {
257 DriveVolume *Drive;
258
259 Drive = new DriveVolume;
260
261 // First thing: build a file list.
262 wprintf (L"Opening volume %s ...", Drives[d].c_str());
263 if (!Drive->Open (Drives[d]))
264 {
265 wprintf (L"FAILED\n\n");
266 delete Drive;
267 continue;
268 }
269 wprintf (L"\n");
270
271 wprintf (L" Getting drive bitmap ...");
272 if (!Drive->GetBitmap ())
273 {
274 wprintf (L"FAILED\n\n");
275 delete Drive;
276 continue;
277 }
278 wprintf (L"\n");
279
280 wprintf (L" Obtaining drive geometry ...");
281 if (!Drive->ObtainInfo ())
282 {
283 wprintf (L"FAILED\n\n");
284 delete Drive;
285 continue;
286 }
287 wprintf (L"\n");
288
289 wprintf (L" Building file database for drive %s ...", Drives[d].c_str());
290 if (!Drive->BuildFileList ())
291 {
292 wprintf (L"FAILED\n\n");
293 delete Drive;
294 continue;
295 }
296 wprintf (L"\n");
297
298 wprintf (L" %u files\n", Drive->GetDBFileCount ());
299
300 // Analyze only?
301 if (DefragMode == DefragAnalyze)
302 {
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
307
308 wprintf (L" Analyzing ...");
309 if (VerboseMode)
310 wprintf (L"\n");
311
312 for (int i = 0; i < Drive->GetDBFileCount(); i++)
313 {
314 uint64 Used;
315 uint64 Slack;
316 FileInfo Info;
317
318 Info = Drive->GetDBFile (i);
319
320 // Compute total used disk space
321 Used = ((Info.Size + Drive->GetClusterSize() - 1) / Drive->GetClusterSize()) * Drive->GetClusterSize();
322 Slack = Used - Info.Size;
323
324 UsedBytes += Used;
325 SlackBytes += Slack;
326 TotalBytes += Info.Size;
327
328 if (VerboseMode)
329 {
330 wprintf (L" %s%s, ", Drive->GetDBDir (Info.DirIndice).c_str(), Info.Name.c_str());
331
332 if (Info.Attributes.AccessDenied)
333 wprintf (L"access was denied\n");
334 else
335 {
336 if (Info.Attributes.Unmovable == 1)
337 wprintf (L"unmovable, ");
338
339 wprintf (L"%I64u bytes, %I64u bytes on disk, %I64u bytes slack, %u fragments\n",
340 Info.Size, Used, Slack, Info.Fragments.size());
341 }
342 }
343
344 if (Info.Fragments.size() > 1)
345 Fragged++;
346 }
347
348 if (!VerboseMode)
349 wprintf (L"\n");
350
351 // TODO: Make it not look like ass
352 wprintf (L"\n");
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());
358 wprintf (L"\n");
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);
362 wprintf (L"\n");
363 wprintf (L" %I64u bytes\n", TotalBytes);
364 wprintf (L" %I64u bytes on disk\n", UsedBytes);
365 wprintf (L" %I64u bytes slack\n", SlackBytes);
366 }
367
368 // Fast defragment!
369 if (DefragMode == DefragFast || DefragMode == DefragExtensive)
370 {
371 uint32 i;
372 uint64 FirstFreeLCN;
373 wchar_t PrintName[80];
374 int Width = 66;
375
376 if (DefragMode == DefragFast)
377 wprintf (L" Performing fast file defragmentation ...\n");
378 else
379 if (DefragMode == DefragExtensive)
380 wprintf (L" Performing extensive file defragmentation\n");
381
382 // Find first free LCN for speedier searches ...
383 Drive->FindFreeRange (0, 1, FirstFreeLCN);
384
385 for (i = 0; i < Drive->GetDBFileCount(); i++)
386 {
387 FileInfo Info;
388 bool Result;
389 uint64 TargetLCN;
390
391 wprintf (L"\r");
392
393 Info = Drive->GetDBFile (i);
394
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);
397
398 // Can't defrag 0 byte files :)
399 if (Info.Fragments.size() == 0)
400 continue;
401
402 // If doing fast defrag, skip non-fragmented files
403 if (Info.Fragments.size() == 1 && DefragMode == DefragFast)
404 continue;
405
406 // Find a place that can fit the file
407 Result = Drive->FindFreeRange (FirstFreeLCN, Info.Clusters, TargetLCN);
408
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
411 // move it.
412 if (DefragMode == DefragExtensive && Info.Fragments.size() == 1)
413 {
414 if (TargetLCN > Info.Fragments[0].StartLCN)
415 continue;
416 }
417
418 // Otherwise, defrag0rize it!
419 if (Result)
420 {
421 bool Success = false;
422
423 if (Drive->MoveFileDumb (i, TargetLCN))
424 Success = true;
425 else
426 { // hmm, look for another area to move it to
427 Result = Drive->FindFreeRange (TargetLCN + 1, Info.Clusters, TargetLCN);
428 if (Result)
429 {
430 if (Drive->MoveFileDumb (i, TargetLCN))
431 Success = true;
432 else
433 { // Try updating the drive bitmap
434 if (Drive->GetBitmap ())
435 {
436 Result = Drive->FindFreeRange (0, Info.Clusters, TargetLCN);
437 if (Result)
438 {
439 if (Drive->MoveFileDumb (i, TargetLCN))
440 Success = true;
441 }
442 }
443 }
444 }
445 }
446
447 if (!Success)
448 wprintf (L"\n -> failed\n");
449
450 Drive->FindFreeRange (0, 1, FirstFreeLCN);
451 }
452 }
453
454 wprintf (L"\n");
455 }
456 wprintf (L"Closing volume %s ...", Drives[d].c_str());
457 delete Drive;
458 wprintf (L"\n");
459 }
460 #endif
461
462 return (0);
463 }
464 #endif