76dc9750987cfcd5bd19c5407580cbdbed6b6bd1
[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 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 */