57a7b26aeff7235287ae3d8a26021cb467070112
[reactos.git] / base / system / chkdsk / chkdsk.c
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 */