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 BOOLEAN Ret; 232 PDWORD percent; 233 PBOOLEAN status; 234 PTEXTOUTPUT output; 235 236 // 237 // We get other types of commands, 238 // but we don't have to pay attention to them 239 // 240 Ret = TRUE; 241 switch (Command) 242 { 243 case UNKNOWN2: 244 ConPuts(StdOut, L"UNKNOWN2\r"); 245 break; 246 247 case UNKNOWN3: 248 ConPuts(StdOut, L"UNKNOWN3\n"); 249 break; 250 251 case UNKNOWN4: 252 ConPuts(StdOut, L"UNKNOWN4\n"); 253 break; 254 255 case UNKNOWN5: 256 ConPuts(StdOut, L"UNKNOWN5\n"); 257 break; 258 259 case FSNOTSUPPORTED: 260 ConPuts(StdOut, L"FSNOTSUPPORTED\n"); 261 break; 262 263 case VOLUMEINUSE: 264 ConPuts(StdOut, L"Volume is in use and cannot be locked\n"); 265 Ret = FALSE; 266 break; 267 268 case UNKNOWN9: 269 ConPuts(StdOut, L"UNKNOWN9\n"); 270 break; 271 272 case UNKNOWNA: 273 ConPuts(StdOut, L"UNKNOWNA\n"); 274 break; 275 276 case UNKNOWNC: 277 ConPuts(StdOut, L"UNKNOWNC\n"); 278 break; 279 280 case UNKNOWND: 281 ConPuts(StdOut, L"UNKNOWND\n"); 282 break; 283 284 case INSUFFICIENTRIGHTS: 285 ConPuts(StdOut, L"INSUFFICIENTRIGHTS\n"); 286 break; 287 288 case STRUCTUREPROGRESS: 289 ConPuts(StdOut, L"STRUCTUREPROGRESS\n"); 290 break; 291 292 case DONEWITHSTRUCTURE: 293 ConPuts(StdOut, L"DONEWITHSTRUCTURE\n"); 294 break; 295 296 case CLUSTERSIZETOOSMALL: 297 ConPuts(StdOut, L"CLUSTERSIZETOOSMALL\n"); 298 break; 299 300 case PROGRESS: 301 percent = (PDWORD)Argument; 302 ConPrintf(StdOut, L"%d percent completed.\r", *percent); 303 break; 304 305 case OUTPUT: 306 output = (PTEXTOUTPUT)Argument; 307 ConPrintf(StdOut, L"%S", output->Output); 308 break; 309 310 case DONE: 311 status = (PBOOLEAN)Argument; 312 if (*status == FALSE) 313 { 314 ConPuts(StdOut, L"Chkdsk was unable to complete successfully.\n\n"); 315 Error = TRUE; 316 } 317 break; 318 } 319 return Ret; 320 } 321 322 #ifndef FMIFS_IMPORT_DLL 323 //---------------------------------------------------------------------- 324 // 325 // LoadFMIFSEntryPoints 326 // 327 // Loads FMIFS.DLL and locates the entry point(s) we are going to use 328 // 329 //---------------------------------------------------------------------- 330 static BOOLEAN 331 LoadFMIFSEntryPoints(VOID) 332 { 333 HMODULE hFmifs = LoadLibraryW(L"fmifs.dll"); 334 if (hFmifs == NULL) 335 return FALSE; 336 337 Chkdsk = (PCHKDSK)GetProcAddress(hFmifs, "Chkdsk"); 338 if (!Chkdsk) 339 { 340 FreeLibrary(hFmifs); 341 return FALSE; 342 } 343 344 return TRUE; 345 } 346 #endif 347 348 349 //---------------------------------------------------------------------- 350 // 351 // WMain 352 // 353 // Engine. Just get command line switches and fire off a chkdsk. This 354 // could also be done in a GUI like Explorer does when you select a 355 // drive and run a check on it. 356 // 357 // We do this in UNICODE because the chkdsk command expects PWCHAR 358 // arguments. 359 // 360 //---------------------------------------------------------------------- 361 int 362 wmain(int argc, WCHAR *argv[]) 363 { 364 int badArg; 365 HANDLE volumeHandle; 366 WCHAR fileSystem[1024]; 367 WCHAR volumeName[1024]; 368 DWORD serialNumber; 369 DWORD flags, maxComponent; 370 371 /* Initialize the Console Standard Streams */ 372 ConInitStdStreams(); 373 374 ConPuts(StdOut, 375 L"\n" 376 L"Chkdskx v1.0.1 by Mark Russinovich\n" 377 L"Systems Internals - http://www.sysinternals.com\n" 378 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n"); 379 380 #ifndef FMIFS_IMPORT_DLL 381 // 382 // Get function pointers 383 // 384 if (!LoadFMIFSEntryPoints()) 385 { 386 ConPuts(StdErr, L"Could not located FMIFS entry points.\n\n"); 387 return -1; 388 } 389 #endif 390 391 // 392 // Parse command line 393 // 394 badArg = ParseCommandLine(argc, argv); 395 if (badArg) 396 { 397 ConPrintf(StdOut, L"Unknown argument: %s\n", argv[badArg]); 398 Usage(argv[0]); 399 return -1; 400 } 401 402 // 403 // Get the drive's format 404 // 405 if (!Drive) 406 { 407 if (!GetCurrentDirectoryW(ARRAYSIZE(CurrentDirectory), CurrentDirectory)) 408 { 409 PrintWin32Error(L"Could not get current directory", GetLastError()); 410 return -1; 411 } 412 } 413 else 414 { 415 wcscpy(CurrentDirectory, Drive); 416 } 417 CurrentDirectory[2] = L'\\'; 418 CurrentDirectory[3] = L'\0'; 419 Drive = CurrentDirectory; 420 421 // 422 // Determine the drive's file system format, which we need to 423 // tell chkdsk 424 // 425 if (!GetVolumeInformationW(Drive, 426 volumeName, 427 ARRAYSIZE(volumeName), 428 &serialNumber, 429 &maxComponent, 430 &flags, 431 fileSystem, 432 ARRAYSIZE(fileSystem))) 433 { 434 PrintWin32Error(L"Could not query volume", GetLastError()); 435 return -1; 436 } 437 438 // 439 // If they want to fix, we need to have access to the drive 440 // 441 if (FixErrors) 442 { 443 swprintf(volumeName, L"\\\\.\\%C:", Drive[0]); 444 volumeHandle = CreateFileW(volumeName, 445 GENERIC_WRITE, 446 0, 447 NULL, 448 OPEN_EXISTING, 449 0, 450 0); 451 if (volumeHandle == INVALID_HANDLE_VALUE) 452 { 453 ConPuts(StdErr, L"Chkdsk cannot run because the volume is in use by another process.\n\n"); 454 return -1; 455 } 456 CloseHandle(volumeHandle); 457 458 // 459 // Can't let the user break out of a chkdsk that can modify the drive 460 // 461 SetConsoleCtrlHandler(CtrlCIntercept, TRUE); 462 } 463 464 // 465 // Just do it 466 // 467 ConPrintf(StdOut, L"The type of file system is %s.\n", fileSystem); 468 Chkdsk(Drive, 469 fileSystem, 470 FixErrors, 471 Verbose, 472 SkipClean, 473 ScanSectors, 474 NULL, 475 NULL, 476 ChkdskCallback); 477 478 if (Error) return -1; 479 return 0; 480 } 481 482 /* EOF */ 483