xref: /reactos/base/system/chkdsk/chkdsk.c (revision fb5d5ecd)
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