1 //====================================================================== 2 // 3 // Chkdskx 4 // 5 // Copyright (c) 1998 Mark Russinovich 6 // Systems Internals 7 // http://www.sysinternals.com 8 // 9 // Chkdsk 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 // 2008 July (Aleksey Bragin) 38 // Cleanup, use ReactOS's fmifs.h 39 // 40 //====================================================================== 41 42 #include <stdio.h> 43 44 /* PSDK/NDK Headers */ 45 #define WIN32_NO_STATUS 46 #include <windef.h> 47 #include <winbase.h> 48 #include <wincon.h> 49 50 #include <conutils.h> 51 52 #define NTOS_MODE_USER 53 #include <ndk/ntndk.h> 54 55 /* FMIFS Public Header */ 56 #include <fmifs/fmifs.h> 57 58 #define FMIFS_IMPORT_DLL 59 60 // 61 // Globals 62 // 63 BOOL Error = FALSE; 64 65 // Switches 66 BOOL FixErrors = FALSE; 67 BOOL SkipClean = FALSE; 68 BOOL ScanSectors = FALSE; 69 BOOL Verbose = FALSE; 70 PWCHAR Drive = NULL; 71 WCHAR CurrentDirectory[1024]; 72 73 #ifndef FMIFS_IMPORT_DLL 74 // 75 // Functions in FMIFS.DLL 76 // 77 PCHKDSK Chkdsk; 78 #endif 79 80 81 //---------------------------------------------------------------------- 82 // 83 // PrintWin32Error 84 // 85 // Takes the win32 error code and prints the text version. 86 // 87 //---------------------------------------------------------------------- 88 static VOID PrintWin32Error(LPWSTR Message, DWORD ErrorCode) 89 { 90 ConPrintf(StdErr, L"%s: ", Message); 91 ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM, 92 NULL, ErrorCode, LANG_USER_DEFAULT); 93 ConPuts(StdErr, L"\n"); 94 } 95 96 97 //-------------------------------------------------------------------- 98 // 99 // CtrlCIntercept 100 // 101 // Intercepts Ctrl-C's so that the program can't be quit with the 102 // disk in an inconsistent state. 103 // 104 //-------------------------------------------------------------------- 105 BOOL 106 WINAPI 107 CtrlCIntercept(DWORD dwCtrlType) 108 { 109 // 110 // Handle the event so that the default handler doesn't 111 // 112 return TRUE; 113 } 114 115 116 //---------------------------------------------------------------------- 117 // 118 // Usage 119 // 120 // Tell the user how to use the program 121 // 122 //---------------------------------------------------------------------- 123 static VOID 124 Usage(PWCHAR ProgramName) 125 { 126 ConPrintf(StdOut, 127 L"Usage: %s [drive:] [-F] [-V] [-R] [-C]\n\n" 128 L"[drive:] Specifies the drive to check.\n" 129 L"-F Fixes errors on the disk.\n" 130 L"-V Displays the full path of every file on the disk.\n" 131 L"-R Locates bad sectors and recovers readable information.\n" 132 L"-C Checks the drive only if it is dirty.\n\n", 133 ProgramName); 134 } 135 136 137 //---------------------------------------------------------------------- 138 // 139 // ParseCommandLine 140 // 141 // Get the switches. 142 // 143 //---------------------------------------------------------------------- 144 static int 145 ParseCommandLine(int argc, WCHAR *argv[]) 146 { 147 int i; 148 BOOLEAN gotFix = FALSE; 149 BOOLEAN gotVerbose = FALSE; 150 BOOLEAN gotClean = FALSE; 151 // BOOLEAN gotScan = FALSE; 152 153 for (i = 1; i < argc; i++) 154 { 155 switch (argv[i][0]) 156 { 157 case L'-': case L'/': 158 159 switch (argv[i][1]) 160 { 161 // case L'?': 162 // Usage(argv[0]); 163 // return i; 164 165 case L'F': case L'f': 166 { 167 if (gotFix) return i; 168 FixErrors = TRUE; 169 gotFix = TRUE; 170 break; 171 } 172 173 case L'V': case L'v': 174 { 175 if (gotVerbose) return i; 176 Verbose = TRUE; 177 gotVerbose = TRUE; 178 break; 179 } 180 181 case L'R': case L'r': 182 { 183 if (gotFix) return i; 184 ScanSectors = TRUE; 185 gotFix = TRUE; 186 break; 187 } 188 189 case L'C': case L'c': 190 { 191 if (gotClean) return i; 192 SkipClean = TRUE; 193 gotClean = TRUE; 194 break; 195 } 196 197 default: 198 return i; 199 } 200 break; 201 202 default: 203 { 204 if (Drive) return i; 205 if (argv[i][1] != L':') return i; 206 207 Drive = argv[i]; 208 break; 209 } 210 } 211 } 212 return 0; 213 } 214 215 216 //---------------------------------------------------------------------- 217 // 218 // ChkdskCallback 219 // 220 // The file system library will call us back with commands that we 221 // can interpret. If we wanted to halt the chkdsk we could return FALSE. 222 // 223 //---------------------------------------------------------------------- 224 BOOLEAN 225 WINAPI 226 ChkdskCallback( 227 CALLBACKCOMMAND Command, 228 DWORD Modifier, 229 PVOID Argument) 230 { 231 PDWORD percent; 232 PBOOLEAN status; 233 PTEXTOUTPUT output; 234 235 // 236 // We get other types of commands, 237 // but we don't have to pay attention to them 238 // 239 switch (Command) 240 { 241 case UNKNOWN2: 242 ConPuts(StdOut, L"UNKNOWN2\r"); 243 break; 244 245 case UNKNOWN3: 246 ConPuts(StdOut, L"UNKNOWN3\n"); 247 break; 248 249 case UNKNOWN4: 250 ConPuts(StdOut, L"UNKNOWN4\n"); 251 break; 252 253 case UNKNOWN5: 254 ConPuts(StdOut, L"UNKNOWN5\n"); 255 break; 256 257 case FSNOTSUPPORTED: 258 ConPuts(StdOut, L"FSNOTSUPPORTED\n"); 259 break; 260 261 case VOLUMEINUSE: 262 ConPuts(StdOut, L"VOLUMEINUSE\n"); 263 break; 264 265 case UNKNOWN9: 266 ConPuts(StdOut, L"UNKNOWN9\n"); 267 break; 268 269 case UNKNOWNA: 270 ConPuts(StdOut, L"UNKNOWNA\n"); 271 break; 272 273 case UNKNOWNC: 274 ConPuts(StdOut, L"UNKNOWNC\n"); 275 break; 276 277 case UNKNOWND: 278 ConPuts(StdOut, L"UNKNOWND\n"); 279 break; 280 281 case INSUFFICIENTRIGHTS: 282 ConPuts(StdOut, L"INSUFFICIENTRIGHTS\n"); 283 break; 284 285 case STRUCTUREPROGRESS: 286 ConPuts(StdOut, L"STRUCTUREPROGRESS\n"); 287 break; 288 289 case DONEWITHSTRUCTURE: 290 ConPuts(StdOut, L"DONEWITHSTRUCTURE\n"); 291 break; 292 293 case CLUSTERSIZETOOSMALL: 294 ConPuts(StdOut, L"CLUSTERSIZETOOSMALL\n"); 295 break; 296 297 case PROGRESS: 298 percent = (PDWORD)Argument; 299 ConPrintf(StdOut, L"%d percent completed.\r", *percent); 300 break; 301 302 case OUTPUT: 303 output = (PTEXTOUTPUT)Argument; 304 ConPrintf(StdOut, L"%S", output->Output); 305 break; 306 307 case DONE: 308 status = (PBOOLEAN)Argument; 309 if (*status == FALSE) 310 { 311 ConPuts(StdOut, L"Chkdsk was unable to complete successfully.\n\n"); 312 Error = TRUE; 313 } 314 break; 315 } 316 return TRUE; 317 } 318 319 #ifndef FMIFS_IMPORT_DLL 320 //---------------------------------------------------------------------- 321 // 322 // LoadFMIFSEntryPoints 323 // 324 // Loads FMIFS.DLL and locates the entry point(s) we are going to use 325 // 326 //---------------------------------------------------------------------- 327 static BOOLEAN 328 LoadFMIFSEntryPoints(VOID) 329 { 330 HMODULE hFmifs = LoadLibraryW(L"fmifs.dll"); 331 if (hFmifs == NULL) 332 return FALSE; 333 334 Chkdsk = (PCHKDSK)GetProcAddress(hFmifs, "Chkdsk"); 335 if (!Chkdsk) 336 { 337 FreeLibrary(hFmifs); 338 return FALSE; 339 } 340 341 return TRUE; 342 } 343 #endif 344 345 346 //---------------------------------------------------------------------- 347 // 348 // WMain 349 // 350 // Engine. Just get command line switches and fire off a chkdsk. This 351 // could also be done in a GUI like Explorer does when you select a 352 // drive and run a check on it. 353 // 354 // We do this in UNICODE because the chkdsk command expects PWCHAR 355 // arguments. 356 // 357 //---------------------------------------------------------------------- 358 int 359 wmain(int argc, WCHAR *argv[]) 360 { 361 int badArg; 362 HANDLE volumeHandle; 363 WCHAR fileSystem[1024]; 364 WCHAR volumeName[1024]; 365 DWORD serialNumber; 366 DWORD flags, maxComponent; 367 368 /* Initialize the Console Standard Streams */ 369 ConInitStdStreams(); 370 371 ConPuts(StdOut, 372 L"\n" 373 L"Chkdskx v1.0.1 by Mark Russinovich\n" 374 L"Systems Internals - http://www.sysinternals.com\n" 375 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n"); 376 377 #ifndef FMIFS_IMPORT_DLL 378 // 379 // Get function pointers 380 // 381 if (!LoadFMIFSEntryPoints()) 382 { 383 ConPuts(StdErr, L"Could not located FMIFS entry points.\n\n"); 384 return -1; 385 } 386 #endif 387 388 // 389 // Parse command line 390 // 391 badArg = ParseCommandLine(argc, argv); 392 if (badArg) 393 { 394 ConPrintf(StdOut, L"Unknown argument: %s\n", argv[badArg]); 395 Usage(argv[0]); 396 return -1; 397 } 398 399 // 400 // Get the drive's format 401 // 402 if (!Drive) 403 { 404 if (!GetCurrentDirectoryW(ARRAYSIZE(CurrentDirectory), CurrentDirectory)) 405 { 406 PrintWin32Error(L"Could not get current directory", GetLastError()); 407 return -1; 408 } 409 } 410 else 411 { 412 wcscpy(CurrentDirectory, Drive); 413 } 414 CurrentDirectory[2] = L'\\'; 415 CurrentDirectory[3] = L'\0'; 416 Drive = CurrentDirectory; 417 418 // 419 // Determine the drive's file system format, which we need to 420 // tell chkdsk 421 // 422 if (!GetVolumeInformationW(Drive, 423 volumeName, 424 ARRAYSIZE(volumeName), 425 &serialNumber, 426 &maxComponent, 427 &flags, 428 fileSystem, 429 ARRAYSIZE(fileSystem))) 430 { 431 PrintWin32Error(L"Could not query volume", GetLastError()); 432 return -1; 433 } 434 435 // 436 // If they want to fix, we need to have access to the drive 437 // 438 if (FixErrors) 439 { 440 swprintf(volumeName, L"\\\\.\\%C:", Drive[0]); 441 volumeHandle = CreateFileW(volumeName, 442 GENERIC_WRITE, 443 0, 444 NULL, 445 OPEN_EXISTING, 446 0, 447 0); 448 if (volumeHandle == INVALID_HANDLE_VALUE) 449 { 450 ConPuts(StdErr, L"Chkdsk cannot run because the volume is in use by another process.\n\n"); 451 return -1; 452 } 453 CloseHandle(volumeHandle); 454 455 // 456 // Can't let the user break out of a chkdsk that can modify the drive 457 // 458 SetConsoleCtrlHandler(CtrlCIntercept, TRUE); 459 } 460 461 // 462 // Just do it 463 // 464 ConPrintf(StdOut, L"The type of file system is %s.\n", fileSystem); 465 Chkdsk(Drive, 466 fileSystem, 467 FixErrors, 468 Verbose, 469 SkipClean, 470 ScanSectors, 471 NULL, 472 NULL, 473 ChkdskCallback); 474 475 if (Error) return -1; 476 return 0; 477 } 478 479 /* EOF */ 480