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 465