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