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 FMIFS_MEDIA_FLAG media = FMIFS_HARDDISK; 364 DWORD driveType; 365 WCHAR fileSystem[1024]; 366 WCHAR volumeName[1024]; 367 WCHAR input[1024]; 368 DWORD serialNumber; 369 DWORD flags, maxComponent; 370 ULARGE_INTEGER freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes; 371 WCHAR szMsg[RC_STRING_MAX_SIZE]; 372 373 /* Initialize the Console Standard Streams */ 374 ConInitStdStreams(); 375 376 ConPuts(StdOut, 377 L"\n" 378 L"Formatx v1.0 by Mark Russinovich\n" 379 L"Systems Internals - http://www.sysinternals.com\n" 380 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n"); 381 382 #ifndef FMIFS_IMPORT_DLL 383 // 384 // Get function pointers 385 // 386 if (!LoadFMIFSEntryPoints()) 387 { 388 ConResPuts(StdErr, STRING_FMIFS_FAIL); 389 return -1; 390 } 391 #endif 392 393 // 394 // Parse command line 395 // 396 badArg = ParseCommandLine(argc, argv); 397 if (badArg) 398 { 399 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]); 400 Usage(argv[0]); 401 return -1; 402 } 403 404 // 405 // Get the drive's format 406 // 407 if (!Drive) 408 { 409 ConResPuts(StdErr, STRING_DRIVE_PARM); 410 Usage(argv[0]); 411 return -1; 412 } 413 else 414 { 415 wcscpy(RootDirectory, Drive); 416 } 417 RootDirectory[2] = L'\\'; 418 RootDirectory[3] = L'\0'; 419 420 // 421 // See if the drive is removable or not 422 // 423 driveType = GetDriveTypeW(RootDirectory); 424 switch (driveType) 425 { 426 case DRIVE_UNKNOWN : 427 K32LoadStringW(GetModuleHandle(NULL), STRING_ERROR_DRIVE_TYPE, szMsg, ARRAYSIZE(szMsg)); 428 PrintWin32Error(szMsg, GetLastError()); 429 return -1; 430 431 case DRIVE_REMOTE: 432 case DRIVE_CDROM: 433 ConResPuts(StdOut, STRING_NO_SUPPORT); 434 return -1; 435 436 case DRIVE_NO_ROOT_DIR: 437 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 438 PrintWin32Error(szMsg, GetLastError()); 439 return -1; 440 441 case DRIVE_REMOVABLE: 442 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]); 443 fgetws(input, ARRAYSIZE(input), stdin); 444 media = FMIFS_FLOPPY; 445 break; 446 447 case DRIVE_FIXED: 448 case DRIVE_RAMDISK: 449 media = FMIFS_HARDDISK; 450 break; 451 } 452 453 // Reject attempts to format the system drive 454 { 455 WCHAR path[MAX_PATH + 1]; 456 UINT rc; 457 rc = GetWindowsDirectoryW(path, MAX_PATH); 458 if (rc == 0 || rc > MAX_PATH) 459 // todo: Report "Unable to query system directory" 460 return -1; 461 if (towlower(path[0]) == towlower(Drive[0])) 462 { 463 // todo: report "Cannot format system drive" 464 ConResPuts(StdOut, STRING_NO_SUPPORT); 465 return -1; 466 } 467 } 468 469 // 470 // Determine the drive's file system format 471 // 472 if (!GetVolumeInformationW(RootDirectory, 473 volumeName, ARRAYSIZE(volumeName), 474 &serialNumber, &maxComponent, &flags, 475 fileSystem, ARRAYSIZE(fileSystem))) 476 { 477 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 478 PrintWin32Error(szMsg, GetLastError()); 479 return -1; 480 } 481 482 if (!GetDiskFreeSpaceExW(RootDirectory, 483 &freeBytesAvailableToCaller, 484 &totalNumberOfBytes, 485 &totalNumberOfFreeBytes)) 486 { 487 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 488 PrintWin32Error(szMsg, GetLastError()); 489 return -1; 490 } 491 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem); 492 493 // 494 // Make sure they want to do this 495 // 496 if (driveType == DRIVE_FIXED) 497 { 498 if (volumeName[0]) 499 { 500 while (TRUE) 501 { 502 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]); 503 fgetws(input, ARRAYSIZE(input), stdin); 504 input[wcslen(input) - 1] = 0; 505 506 if (!wcsicmp(input, volumeName)) 507 break; 508 509 ConResPuts(StdOut, STRING_ERROR_LABEL); 510 } 511 } 512 513 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]); 514 515 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg)); 516 while (TRUE) 517 { 518 fgetws(input, ARRAYSIZE(input), stdin); 519 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break; 520 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0) 521 { 522 ConPuts(StdOut, L"\n"); 523 return 0; 524 } 525 } 526 } 527 528 // 529 // Tell the user we're doing a long format if appropriate 530 // 531 if (!QuickFormat) 532 { 533 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg)); 534 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 535 { 536 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 537 } 538 else 539 { 540 ConPrintf(StdOut, L"%s %.1fM\n", szMsg, 541 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 542 } 543 } 544 else 545 { 546 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg)); 547 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 548 { 549 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 550 } 551 else 552 { 553 ConPrintf(StdOut, L"%s %.2fM\n", szMsg, 554 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 555 } 556 ConResPuts(StdOut, STRING_CREATE_FSYS); 557 } 558 559 // 560 // Format away! 561 // 562 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat, 563 ClusterSize, FormatExCallback); 564 if (Error) return -1; 565 ConPuts(StdOut, L"\n"); 566 ConResPuts(StdOut, STRING_FMT_COMPLETE); 567 568 // 569 // Enable compression if desired 570 // 571 if (CompressDrive) 572 { 573 if (!EnableVolumeCompression(RootDirectory, TRUE)) 574 ConResPuts(StdOut, STRING_VOL_COMPRESS); 575 } 576 577 // 578 // Get the label if we don't have it 579 // 580 if (!GotALabel) 581 { 582 ConResPuts(StdOut, STRING_ENTER_LABEL); 583 fgetws(input, ARRAYSIZE(LabelString), stdin); 584 585 input[wcslen(input) - 1] = 0; 586 if (!SetVolumeLabelW(RootDirectory, input)) 587 { 588 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg)); 589 PrintWin32Error(szMsg, GetLastError()); 590 return -1; 591 } 592 } 593 594 if (!GetVolumeInformationW(RootDirectory, 595 volumeName, ARRAYSIZE(volumeName), 596 &serialNumber, &maxComponent, &flags, 597 fileSystem, ARRAYSIZE(fileSystem))) 598 { 599 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 600 PrintWin32Error(szMsg, GetLastError()); 601 return -1; 602 } 603 604 // 605 // Print out some stuff including the formatted size 606 // 607 if (!GetDiskFreeSpaceExW(RootDirectory, 608 &freeBytesAvailableToCaller, 609 &totalNumberOfBytes, 610 &totalNumberOfFreeBytes)) 611 { 612 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 613 PrintWin32Error(szMsg, GetLastError()); 614 return -1; 615 } 616 617 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart, 618 totalNumberOfFreeBytes.QuadPart); 619 620 // 621 // Get the drive's serial number 622 // 623 if (!GetVolumeInformationW(RootDirectory, 624 volumeName, ARRAYSIZE(volumeName), 625 &serialNumber, &maxComponent, &flags, 626 fileSystem, ARRAYSIZE(fileSystem))) 627 { 628 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 629 PrintWin32Error(szMsg, GetLastError()); 630 return -1; 631 } 632 ConResPrintf(StdOut, STRING_SERIAL_NUMBER, 633 (unsigned int)(serialNumber >> 16), 634 (unsigned int)(serialNumber & 0xFFFF)); 635 636 return 0; 637 } 638 639 /* EOF */ 640