1 //====================================================================== 2 // 3 // Formatx 4 // 5 // Copyright (c) 1998 Mark Russinovich 6 // Systems Internals 7 // http://www.sysinternals.com 8 // 9 // Format clone that demonstrates the use of the FMIFS file system 10 // utility library. 11 // 12 // -------------------------------------------------------------------- 13 // 14 // This software is free software; you can redistribute it and/or 15 // modify it under the terms of the GNU Library General Public License as 16 // published by the Free Software Foundation; either version 2 of the 17 // License, or (at your option) any later version. 18 // 19 // This software is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 // Library General Public License for more details. 23 // 24 // You should have received a copy of the GNU Library General Public 25 // License along with this software; see the file COPYING.LIB. If 26 // not, write to the Free Software Foundation, Inc., 675 Mass Ave, 27 // Cambridge, MA 02139, USA. 28 // 29 // -------------------------------------------------------------------- 30 // 31 // 1999 February (Emanuele Aliberti) 32 // Adapted for ReactOS and lcc-win32. 33 // 34 // 1999 April (Emanuele Aliberti) 35 // Adapted for ReactOS and egcs. 36 // 37 // 2003 April (Casper S. Hornstrup) 38 // Reintegration. 39 // 40 //====================================================================== 41 42 #include <stdio.h> 43 #include <tchar.h> 44 45 /* PSDK/NDK Headers */ 46 #define WIN32_NO_STATUS 47 #include <windef.h> 48 #include <winbase.h> 49 50 #include <conutils.h> 51 52 #define NTOS_MODE_USER 53 #include <ndk/rtlfuncs.h> 54 55 /* FMIFS Public Header */ 56 #include <fmifs/fmifs.h> 57 58 #include "resource.h" 59 60 #define FMIFS_IMPORT_DLL 61 62 // Globals 63 BOOL Error = FALSE; 64 65 // Switches 66 BOOL QuickFormat = FALSE; 67 DWORD ClusterSize = 0; 68 BOOL CompressDrive = FALSE; 69 BOOL GotALabel = FALSE; 70 PWCHAR Label = L""; 71 PWCHAR Drive = NULL; 72 PWCHAR FileSystem = L"FAT"; 73 74 WCHAR RootDirectory[MAX_PATH]; 75 WCHAR LabelString[12]; 76 77 #ifndef FMIFS_IMPORT_DLL 78 // 79 // Functions in FMIFS.DLL 80 // 81 PFORMATEX FormatEx; 82 PENABLEVOLUMECOMPRESSION EnableVolumeCompression; 83 PQUERYAVAILABLEFILESYSTEMFORMAT QueryAvailableFileSystemFormat; 84 #endif 85 86 87 // 88 // Size array 89 // 90 typedef struct { 91 WCHAR SizeString[16]; 92 DWORD ClusterSize; 93 } SIZEDEFINITION, *PSIZEDEFINITION; 94 95 SIZEDEFINITION LegalSizes[] = { 96 { L"512", 512 }, 97 { L"1024", 1024 }, 98 { L"2048", 2048 }, 99 { L"4096", 4096 }, 100 { L"8192", 8192 }, 101 { L"16K", 16384 }, 102 { L"32K", 32768 }, 103 { L"64K", 65536 }, 104 { L"128K", 65536 * 2 }, 105 { L"256K", 65536 * 4 }, 106 { L"", 0 }, 107 }; 108 109 110 //---------------------------------------------------------------------- 111 // 112 // PrintWin32Error 113 // 114 // Takes the win32 error code and prints the text version. 115 // 116 //---------------------------------------------------------------------- 117 static VOID PrintWin32Error(LPWSTR Message, DWORD ErrorCode) 118 { 119 ConPrintf(StdErr, L"%s: ", Message); 120 ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM, 121 NULL, ErrorCode, LANG_USER_DEFAULT); 122 ConPuts(StdErr, L"\n"); 123 } 124 125 126 //---------------------------------------------------------------------- 127 // 128 // ParseCommandLine 129 // 130 // Get the switches. 131 // 132 //---------------------------------------------------------------------- 133 static int ParseCommandLine(int argc, WCHAR *argv[]) 134 { 135 int i, j; 136 BOOLEAN gotFormat = FALSE; 137 BOOLEAN gotQuick = FALSE; 138 BOOLEAN gotSize = FALSE; 139 BOOLEAN gotLabel = FALSE; 140 BOOLEAN gotCompressed = FALSE; 141 142 for (i = 1; i < argc; i++) 143 { 144 switch (argv[i][0]) 145 { 146 case L'-': case L'/': 147 148 if (!_wcsnicmp(&argv[i][1], L"FS:", 3)) 149 { 150 if (gotFormat) return -1; 151 FileSystem = &argv[i][4]; 152 gotFormat = TRUE; 153 } 154 else if (!_wcsnicmp(&argv[i][1], L"A:", 2)) 155 { 156 if (gotSize) return -1; 157 j = 0; 158 while (LegalSizes[j].ClusterSize && 159 wcsicmp(LegalSizes[j].SizeString, &argv[i][3])) 160 { 161 j++; 162 } 163 164 if (!LegalSizes[j].ClusterSize) return i; 165 ClusterSize = LegalSizes[j].ClusterSize; 166 gotSize = TRUE; 167 } 168 else if (!_wcsnicmp(&argv[i][1], L"V:", 2)) 169 { 170 if (gotLabel) return -1; 171 Label = &argv[i][3]; 172 gotLabel = TRUE; 173 GotALabel = TRUE; 174 } 175 else if (!wcsicmp(&argv[i][1], L"Q")) 176 { 177 if (gotQuick) return -1; 178 QuickFormat = TRUE; 179 gotQuick = TRUE; 180 } 181 else if (!wcsicmp(&argv[i][1], L"C")) 182 { 183 if (gotCompressed) return -1; 184 CompressDrive = TRUE; 185 gotCompressed = TRUE; 186 } 187 else 188 { 189 return i; 190 } 191 break; 192 193 default: 194 { 195 if (Drive) return i; 196 if (argv[i][1] != L':') return i; 197 198 Drive = argv[i]; 199 break; 200 } 201 } 202 } 203 return 0; 204 } 205 206 //---------------------------------------------------------------------- 207 // 208 // FormatExCallback 209 // 210 // The file system library will call us back with commands that we 211 // can interpret. If we wanted to halt the chkdsk we could return FALSE. 212 // 213 //---------------------------------------------------------------------- 214 BOOLEAN WINAPI 215 FormatExCallback( 216 CALLBACKCOMMAND Command, 217 ULONG Modifier, 218 PVOID Argument) 219 { 220 PDWORD percent; 221 PTEXTOUTPUT output; 222 PBOOLEAN status; 223 224 // 225 // We get other types of commands, but we don't have to pay attention to them 226 // 227 switch (Command) 228 { 229 case PROGRESS: 230 percent = (PDWORD)Argument; 231 ConResPrintf(StdOut, STRING_COMPLETE, *percent); 232 break; 233 234 case OUTPUT: 235 output = (PTEXTOUTPUT)Argument; 236 ConPrintf(StdOut, L"%S\n", output->Output); 237 break; 238 239 case DONE: 240 status = (PBOOLEAN)Argument; 241 if (*status == FALSE) 242 { 243 ConResPuts(StdOut, STRING_FORMAT_FAIL); 244 Error = TRUE; 245 } 246 break; 247 248 case DONEWITHSTRUCTURE: 249 case UNKNOWN2: 250 case UNKNOWN3: 251 case UNKNOWN4: 252 case UNKNOWN5: 253 case INSUFFICIENTRIGHTS: 254 case FSNOTSUPPORTED: 255 case VOLUMEINUSE: 256 case UNKNOWN9: 257 case UNKNOWNA: 258 case UNKNOWNC: 259 case UNKNOWND: 260 case STRUCTUREPROGRESS: 261 case CLUSTERSIZETOOSMALL: 262 ConResPuts(StdOut, STRING_NO_SUPPORT); 263 return FALSE; 264 } 265 return TRUE; 266 } 267 268 #ifndef FMIFS_IMPORT_DLL 269 //---------------------------------------------------------------------- 270 // 271 // LoadFMIFSEntryPoints 272 // 273 // Loads FMIFS.DLL and locates the entry point(s) we are going to use 274 // 275 //---------------------------------------------------------------------- 276 static BOOLEAN LoadFMIFSEntryPoints(VOID) 277 { 278 HMODULE hFmifs = LoadLibraryW( L"fmifs.dll"); 279 if (hFmifs == NULL) 280 return FALSE; 281 282 FormatEx = (PFORMATEX)GetProcAddress(hFmifs, "FormatEx"); 283 if (!FormatEx) 284 { 285 FreeLibrary(hFmifs); 286 return FALSE; 287 } 288 289 EnableVolumeCompression = (PENABLEVOLUMECOMPRESSION)GetProcAddress(hFmifs, "EnableVolumeCompression"); 290 if (!EnableVolumeCompression) 291 { 292 FreeLibrary(hFmifs); 293 return FALSE; 294 } 295 296 QueryAvailableFileSystemFormat = (PQUERYAVAILABLEFILESYSTEMFORMAT)GetProcAddress(hFmifs, "QueryAvailableFileSystemFormat"); 297 if (!QueryAvailableFileSystemFormat) 298 { 299 FreeLibrary(hFmifs); 300 return FALSE; 301 } 302 303 return TRUE; 304 } 305 #endif 306 307 308 //---------------------------------------------------------------------- 309 // 310 // Usage 311 // 312 // Tell the user how to use the program 313 // 314 //---------------------------------------------------------------------- 315 static VOID Usage(LPWSTR ProgramName) 316 { 317 WCHAR szMsg[RC_STRING_MAX_SIZE]; 318 WCHAR szFormats[MAX_PATH]; 319 WCHAR szFormatW[MAX_PATH]; 320 DWORD Index = 0; 321 BYTE dummy; 322 BOOLEAN latestVersion; 323 324 K32LoadStringW(GetModuleHandle(NULL), STRING_HELP, szMsg, ARRAYSIZE(szMsg)); 325 326 #ifndef FMIFS_IMPORT_DLL 327 if (!LoadFMIFSEntryPoints()) 328 { 329 ConPrintf(StdOut, szMsg, ProgramName, L""); 330 return; 331 } 332 #endif 333 334 szFormats[0] = 0; 335 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &latestVersion)) 336 { 337 if (!latestVersion) 338 continue; 339 if (szFormats[0]) 340 wcscat(szFormats, L", "); 341 342 wcscat(szFormats, szFormatW); 343 } 344 ConPrintf(StdOut, szMsg, ProgramName, szFormats); 345 } 346 347 348 //---------------------------------------------------------------------- 349 // 350 // WMain 351 // 352 // Engine. Just get command line switches and fire off a format. This 353 // could also be done in a GUI like Explorer does when you select a 354 // drive and run a check on it. 355 // 356 // We do this in UNICODE because the chkdsk command expects PWCHAR 357 // arguments. 358 // 359 //---------------------------------------------------------------------- 360 int wmain(int argc, WCHAR *argv[]) 361 { 362 int badArg; 363 DEVICE_INFORMATION DeviceInformation = {0}; 364 FMIFS_MEDIA_FLAG media = FMIFS_HARDDISK; 365 DWORD driveType; 366 WCHAR fileSystem[1024]; 367 WCHAR volumeName[1024] = {0}; 368 WCHAR input[1024]; 369 DWORD serialNumber; 370 DWORD flags, maxComponent; 371 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes; 372 WCHAR szMsg[RC_STRING_MAX_SIZE]; 373 374 /* Initialize the Console Standard Streams */ 375 ConInitStdStreams(); 376 377 ConPuts(StdOut, 378 L"\n" 379 L"Formatx v1.0 by Mark Russinovich\n" 380 L"Systems Internals - http://www.sysinternals.com\n" 381 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n"); 382 383 #ifndef FMIFS_IMPORT_DLL 384 // 385 // Get function pointers 386 // 387 if (!LoadFMIFSEntryPoints()) 388 { 389 ConResPuts(StdErr, STRING_FMIFS_FAIL); 390 return -1; 391 } 392 #endif 393 394 // 395 // Parse command line 396 // 397 badArg = ParseCommandLine(argc, argv); 398 if (badArg) 399 { 400 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]); 401 Usage(argv[0]); 402 return -1; 403 } 404 405 // 406 // Get the drive's format 407 // 408 if (!Drive) 409 { 410 ConResPuts(StdErr, STRING_DRIVE_PARM); 411 Usage(argv[0]); 412 return -1; 413 } 414 else 415 { 416 wcscpy(RootDirectory, Drive); 417 } 418 RootDirectory[2] = L'\\'; 419 RootDirectory[3] = L'\0'; 420 421 // 422 // See if the drive is removable or not 423 // 424 driveType = GetDriveTypeW(RootDirectory); 425 switch (driveType) 426 { 427 case DRIVE_UNKNOWN : 428 K32LoadStringW(GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, szMsg, ARRAYSIZE(szMsg)); 429 PrintWin32Error(szMsg, GetLastError()); 430 return -1; 431 432 case DRIVE_REMOTE: 433 case DRIVE_CDROM: 434 ConResPuts(StdOut, STRING_NO_SUPPORT); 435 return -1; 436 437 case DRIVE_NO_ROOT_DIR: 438 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 439 PrintWin32Error(szMsg, GetLastError()); 440 return -1; 441 442 case DRIVE_REMOVABLE: 443 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]); 444 fgetws(input, ARRAYSIZE(input), stdin); 445 media = FMIFS_FLOPPY; 446 break; 447 448 case DRIVE_FIXED: 449 case DRIVE_RAMDISK: 450 media = FMIFS_HARDDISK; 451 break; 452 } 453 454 // Reject attempts to format the system drive 455 { 456 WCHAR path[MAX_PATH + 1]; 457 UINT rc; 458 rc = GetWindowsDirectoryW(path, MAX_PATH); 459 if (rc == 0 || rc > MAX_PATH) 460 // todo: Report "Unable to query system directory" 461 return -1; 462 if (towlower(path[0]) == towlower(Drive[0])) 463 { 464 // todo: report "Cannot format system drive" 465 ConResPuts(StdOut, STRING_NO_SUPPORT); 466 return -1; 467 } 468 } 469 470 // 471 // Determine the drive's file system format 472 // 473 if (!GetVolumeInformationW(RootDirectory, 474 volumeName, ARRAYSIZE(volumeName), 475 &serialNumber, &maxComponent, &flags, 476 fileSystem, ARRAYSIZE(fileSystem))) 477 { 478 if (GetLastError() == ERROR_UNRECOGNIZED_VOLUME) 479 { 480 wcscpy(fileSystem, L"RAW"); 481 } 482 else 483 { 484 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 485 PrintWin32Error(szMsg, GetLastError()); 486 return -1; 487 } 488 } 489 490 if (QueryDeviceInformation(RootDirectory, 491 &DeviceInformation, 492 sizeof(DeviceInformation))) 493 { 494 totalNumberOfBytes.QuadPart = DeviceInformation.SectorSize * 495 DeviceInformation.SectorCount.QuadPart; 496 } 497 498 /* QueryDeviceInformation returns more accurate volume length and works with 499 * unformatted volumes, however it will NOT return volume length on XP/2003. 500 * Fallback to GetFreeDiskSpaceExW if we did not get any volume length. */ 501 if (totalNumberOfBytes.QuadPart == 0 && 502 !GetDiskFreeSpaceExW(RootDirectory, 503 &freeBytesAvailableToCaller, 504 &totalNumberOfBytes, 505 &totalNumberOfFreeBytes)) 506 { 507 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 508 PrintWin32Error(szMsg, GetLastError()); 509 return -1; 510 } 511 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem); 512 513 // 514 // Make sure they want to do this 515 // 516 if (driveType == DRIVE_FIXED) 517 { 518 if (volumeName[0]) 519 { 520 while (TRUE) 521 { 522 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]); 523 fgetws(input, ARRAYSIZE(input), stdin); 524 input[wcslen(input) - 1] = 0; 525 526 if (!wcsicmp(input, volumeName)) 527 break; 528 529 ConResPuts(StdOut, STRING_ERROR_LABEL); 530 } 531 } 532 533 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]); 534 535 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg)); 536 while (TRUE) 537 { 538 fgetws(input, ARRAYSIZE(input), stdin); 539 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break; 540 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0) 541 { 542 ConPuts(StdOut, L"\n"); 543 return 0; 544 } 545 } 546 } 547 548 // 549 // Tell the user we're doing a long format if appropriate 550 // 551 if (!QuickFormat) 552 { 553 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg)); 554 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 555 { 556 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 557 } 558 else 559 { 560 ConPrintf(StdOut, L"%s %.1fM\n", szMsg, 561 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 562 } 563 } 564 else 565 { 566 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg)); 567 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 568 { 569 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 570 } 571 else 572 { 573 ConPrintf(StdOut, L"%s %.2fM\n", szMsg, 574 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 575 } 576 ConResPuts(StdOut, STRING_CREATE_FSYS); 577 } 578 579 // 580 // Format away! 581 // 582 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat, 583 ClusterSize, FormatExCallback); 584 if (Error) return -1; 585 ConPuts(StdOut, L"\n"); 586 ConResPuts(StdOut, STRING_FMT_COMPLETE); 587 588 // 589 // Enable compression if desired 590 // 591 if (CompressDrive) 592 { 593 if (!EnableVolumeCompression(RootDirectory, TRUE)) 594 ConResPuts(StdOut, STRING_VOL_COMPRESS); 595 } 596 597 // 598 // Get the label if we don't have it 599 // 600 if (!GotALabel) 601 { 602 ConResPuts(StdOut, STRING_ENTER_LABEL); 603 fgetws(input, ARRAYSIZE(LabelString), stdin); 604 605 input[wcslen(input) - 1] = 0; 606 if (!SetVolumeLabelW(RootDirectory, input)) 607 { 608 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg)); 609 PrintWin32Error(szMsg, GetLastError()); 610 return -1; 611 } 612 } 613 614 if (!GetVolumeInformationW(RootDirectory, 615 volumeName, ARRAYSIZE(volumeName), 616 &serialNumber, &maxComponent, &flags, 617 fileSystem, ARRAYSIZE(fileSystem))) 618 { 619 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 620 PrintWin32Error(szMsg, GetLastError()); 621 return -1; 622 } 623 624 // 625 // Print out some stuff including the formatted size 626 // 627 if (!GetDiskFreeSpaceExW(RootDirectory, 628 &freeBytesAvailableToCaller, 629 &totalNumberOfBytes, 630 &totalNumberOfFreeBytes)) 631 { 632 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 633 PrintWin32Error(szMsg, GetLastError()); 634 return -1; 635 } 636 637 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart, 638 totalNumberOfFreeBytes.QuadPart); 639 640 // 641 // Get the drive's serial number 642 // 643 if (!GetVolumeInformationW(RootDirectory, 644 volumeName, ARRAYSIZE(volumeName), 645 &serialNumber, &maxComponent, &flags, 646 fileSystem, ARRAYSIZE(fileSystem))) 647 { 648 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 649 PrintWin32Error(szMsg, GetLastError()); 650 return -1; 651 } 652 ConResPrintf(StdOut, STRING_SERIAL_NUMBER, 653 (unsigned int)(serialNumber >> 16), 654 (unsigned int)(serialNumber & 0xFFFF)); 655 656 return 0; 657 } 658 659 /* EOF */ 660