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