Reactos
at master 647 lines 18 kB view raw
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 63BOOL Error = FALSE; 64 65// Switches 66BOOL QuickFormat = FALSE; 67DWORD ClusterSize = 0; 68BOOL CompressDrive = FALSE; 69BOOL GotALabel = FALSE; 70PWCHAR Label = L""; 71PWCHAR Drive = NULL; 72PWCHAR FileSystem = L"FAT"; 73 74WCHAR RootDirectory[MAX_PATH]; 75WCHAR LabelString[12]; 76 77#ifndef FMIFS_IMPORT_DLL 78// 79// Functions in FMIFS.DLL 80// 81PFORMATEX FormatEx; 82PENABLEVOLUMECOMPRESSION EnableVolumeCompression; 83PQUERYAVAILABLEFILESYSTEMFORMAT QueryAvailableFileSystemFormat; 84#endif 85 86 87// 88// Size array 89// 90typedef struct { 91 WCHAR SizeString[16]; 92 DWORD ClusterSize; 93} SIZEDEFINITION, *PSIZEDEFINITION; 94 95SIZEDEFINITION 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//---------------------------------------------------------------------- 117static 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//---------------------------------------------------------------------- 133static 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//---------------------------------------------------------------------- 214BOOLEAN WINAPI 215FormatExCallback( 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//---------------------------------------------------------------------- 276static 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//---------------------------------------------------------------------- 315static 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 szFormats[0] = 0; 327 while (QueryAvailableFileSystemFormat(Index++, szFormatW, &dummy, &dummy, &latestVersion)) 328 { 329 if (!latestVersion) 330 continue; 331 if (szFormats[0]) 332 wcscat(szFormats, L", "); 333 334 wcscat(szFormats, szFormatW); 335 } 336 ConPrintf(StdOut, szMsg, ProgramName, szFormats); 337} 338 339 340//---------------------------------------------------------------------- 341// 342// WMain 343// 344// Engine. Just get command line switches and fire off a format. This 345// could also be done in a GUI like Explorer does when you select a 346// drive and run a check on it. 347// 348// We do this in UNICODE because the chkdsk command expects PWCHAR 349// arguments. 350// 351//---------------------------------------------------------------------- 352int wmain(int argc, WCHAR *argv[]) 353{ 354 int badArg; 355 DEVICE_INFORMATION DeviceInformation; 356 FMIFS_MEDIA_FLAG media = FMIFS_HARDDISK; 357 DWORD driveType; 358 WCHAR fileSystem[1024]; 359 WCHAR volumeName[1024]; 360 WCHAR input[1024]; 361 DWORD serialNumber; 362 ULARGE_INTEGER totalNumberOfBytes, totalNumberOfFreeBytes; 363 WCHAR szMsg[RC_STRING_MAX_SIZE]; 364 DWORD dwError; 365 366 /* Initialize the Console Standard Streams */ 367 ConInitStdStreams(); 368 369 ConPuts(StdOut, 370 L"\n" 371 L"Formatx v1.0 by Mark Russinovich\n" 372 L"Systems Internals - http://www.sysinternals.com\n" 373 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n"); 374 375#ifndef FMIFS_IMPORT_DLL 376 // 377 // Get function pointers 378 // 379 if (!LoadFMIFSEntryPoints()) 380 { 381 ConResPuts(StdErr, STRING_FMIFS_FAIL); 382 return -1; 383 } 384#endif 385 386 // 387 // Parse command line 388 // 389 badArg = ParseCommandLine(argc, argv); 390 if (badArg) 391 { 392 ConResPrintf(StdErr, STRING_UNKNOW_ARG, argv[badArg]); 393 Usage(argv[0]); 394 return -1; 395 } 396 397 // 398 // Get the drive's format 399 // 400 if (!Drive) 401 { 402 ConResPuts(StdErr, STRING_DRIVE_PARM); 403 Usage(argv[0]); 404 return -1; 405 } 406 else 407 { 408 wcscpy(RootDirectory, Drive); 409 } 410 RootDirectory[2] = L'\\'; 411 RootDirectory[3] = L'\0'; 412 413 // 414 // See if the drive is removable or not 415 // 416 driveType = GetDriveTypeW(RootDirectory); 417 switch (driveType) 418 { 419 case DRIVE_UNKNOWN: 420 case DRIVE_NO_ROOT_DIR: // This case used to report STRING_NO_VOLUME, which has no ".\n". 421 ConResPuts(StdErr, STRING_ERROR_DRIVE_TYPE); 422 return -1; 423 424 case DRIVE_REMOTE: 425 case DRIVE_CDROM: 426 ConResPuts(StdOut, STRING_NO_SUPPORT); 427 return -1; 428 429 case DRIVE_REMOVABLE: 430 ConResPrintf(StdOut, STRING_INSERT_DISK, RootDirectory[0]); 431 fgetws(input, ARRAYSIZE(input), stdin); 432 media = FMIFS_FLOPPY; 433 break; 434 435 case DRIVE_FIXED: 436 case DRIVE_RAMDISK: 437 media = FMIFS_HARDDISK; 438 break; 439 } 440 441 // Reject attempts to format the system drive 442 { 443 WCHAR path[MAX_PATH + 1]; 444 UINT rc; 445 rc = GetWindowsDirectoryW(path, MAX_PATH); 446 if (rc == 0 || rc > MAX_PATH) 447 // todo: Report "Unable to query system directory" 448 return -1; 449 if (towlower(path[0]) == towlower(Drive[0])) 450 { 451 // todo: report "Cannot format system drive" 452 ConResPuts(StdOut, STRING_NO_SUPPORT); 453 return -1; 454 } 455 } 456 457 // 458 // Get the existing name and file system, and print out the latter 459 // 460 if (!GetVolumeInformationW(RootDirectory, 461 volumeName, ARRAYSIZE(volumeName), 462 NULL, NULL, NULL, 463 fileSystem, ARRAYSIZE(fileSystem))) 464 { 465 dwError = GetLastError(); 466 if (dwError == ERROR_UNRECOGNIZED_VOLUME) 467 { 468 // Unformatted volume 469 volumeName[0] = UNICODE_NULL; 470 wcscpy(fileSystem, L"RAW"); 471 } 472 else 473 { 474 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 475 PrintWin32Error(szMsg, dwError); 476 return -1; 477 } 478 } 479 480 ConResPrintf(StdOut, STRING_FILESYSTEM, fileSystem); 481 482 if (!QueryDeviceInformation(RootDirectory, &DeviceInformation, sizeof(DeviceInformation))) 483 { 484 totalNumberOfBytes.QuadPart = 0; 485 } 486 else 487 { 488 totalNumberOfBytes.QuadPart = DeviceInformation.SectorSize * 489 DeviceInformation.SectorCount.QuadPart; 490 } 491 492 /* QueryDeviceInformation returns more accurate volume length and works with 493 * unformatted volumes, however it will NOT return volume length on XP/2003. 494 * Fallback to GetFreeDiskSpaceExW if we did not get any volume length. */ 495 if (totalNumberOfBytes.QuadPart == 0 && 496 !GetDiskFreeSpaceExW(RootDirectory, 497 NULL, 498 &totalNumberOfBytes, 499 NULL)) 500 { 501 dwError = GetLastError(); 502 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 503 PrintWin32Error(szMsg, dwError); 504 return -1; 505 } 506 507 // 508 // Make sure they want to do this 509 // 510 if (driveType == DRIVE_FIXED) 511 { 512 if (volumeName[0]) 513 { 514 while (TRUE) 515 { 516 ConResPrintf(StdOut, STRING_LABEL_NAME_EDIT, RootDirectory[0]); 517 fgetws(input, ARRAYSIZE(input), stdin); 518 input[wcslen(input) - 1] = 0; 519 520 if (!wcsicmp(input, volumeName)) 521 break; 522 523 ConResPuts(StdOut, STRING_ERROR_LABEL); 524 } 525 } 526 527 ConResPrintf(StdOut, STRING_YN_FORMAT, RootDirectory[0]); 528 529 K32LoadStringW(GetModuleHandle(NULL), STRING_YES_NO_FAQ, szMsg, ARRAYSIZE(szMsg)); 530 while (TRUE) 531 { 532 fgetws(input, ARRAYSIZE(input), stdin); 533 if (_wcsnicmp(&input[0], &szMsg[0], 1) == 0) break; 534 if (_wcsnicmp(&input[0], &szMsg[1], 1) == 0) 535 { 536 ConPuts(StdOut, L"\n"); 537 return 0; 538 } 539 } 540 } 541 542 // 543 // Tell the user we're doing a long format if appropriate 544 // 545 if (!QuickFormat) 546 { 547 K32LoadStringW(GetModuleHandle(NULL), STRING_VERIFYING, szMsg, ARRAYSIZE(szMsg)); 548 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 549 { 550 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 551 } 552 else 553 { 554 ConPrintf(StdOut, L"%s %.1fM\n", szMsg, 555 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 556 } 557 } 558 else 559 { 560 K32LoadStringW(GetModuleHandle(NULL), STRING_FAST_FMT, szMsg, ARRAYSIZE(szMsg)); 561 if (totalNumberOfBytes.QuadPart > 1024*1024*10) 562 { 563 ConPrintf(StdOut, L"%s %luM\n", szMsg, (DWORD)(totalNumberOfBytes.QuadPart/(1024*1024))); 564 } 565 else 566 { 567 ConPrintf(StdOut, L"%s %.2fM\n", szMsg, 568 ((float)(LONGLONG)totalNumberOfBytes.QuadPart)/(float)(1024.0*1024.0)); 569 } 570 ConResPuts(StdOut, STRING_CREATE_FSYS); 571 } 572 573 // 574 // Format away! 575 // 576 FormatEx(RootDirectory, media, FileSystem, Label, QuickFormat, 577 ClusterSize, FormatExCallback); 578 if (Error) return -1; 579 ConPuts(StdOut, L"\n"); 580 ConResPuts(StdOut, STRING_FMT_COMPLETE); 581 582 // 583 // Enable compression if desired 584 // 585 if (CompressDrive) 586 { 587 if (!EnableVolumeCompression(RootDirectory, TRUE)) 588 ConResPuts(StdOut, STRING_VOL_COMPRESS); 589 } 590 591 // 592 // Get the label if we don't have it 593 // 594 if (!GotALabel) 595 { 596 ConResPuts(StdOut, STRING_ENTER_LABEL); 597 fgetws(input, ARRAYSIZE(LabelString), stdin); 598 599 input[wcslen(input) - 1] = 0; 600 if (!SetVolumeLabelW(RootDirectory, input)) 601 { 602 dwError = GetLastError(); 603 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_LABEL, szMsg, ARRAYSIZE(szMsg)); 604 PrintWin32Error(szMsg, dwError); 605 return -1; 606 } 607 } 608 609 // 610 // Get and print out some stuff including the formatted size 611 // 612 if (!GetDiskFreeSpaceExW(RootDirectory, 613 NULL, 614 &totalNumberOfBytes, 615 &totalNumberOfFreeBytes)) 616 { 617 dwError = GetLastError(); 618 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME_SIZE, szMsg, ARRAYSIZE(szMsg)); 619 PrintWin32Error(szMsg, dwError); 620 return -1; 621 } 622 623 ConResPrintf(StdOut, STRING_FREE_SPACE, totalNumberOfBytes.QuadPart, 624 totalNumberOfFreeBytes.QuadPart); 625 626 // 627 // Get and print out the new serial number 628 // 629 if (!GetVolumeInformationW(RootDirectory, 630 NULL, 0, 631 &serialNumber, NULL, NULL, 632 NULL, 0)) 633 { 634 dwError = GetLastError(); 635 K32LoadStringW(GetModuleHandle(NULL), STRING_NO_VOLUME, szMsg, ARRAYSIZE(szMsg)); 636 PrintWin32Error(szMsg, dwError); 637 return -1; 638 } 639 640 ConResPrintf(StdOut, STRING_SERIAL_NUMBER, 641 (unsigned int)(serialNumber >> 16), 642 (unsigned int)(serialNumber & 0xFFFF)); 643 644 return 0; 645} 646 647/* EOF */