[AUTOCHK][CHKDSK]: Minor fixes / formatting. Really add chkdsk to build.
[reactos.git] / reactos / 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 #define NTOS_MODE_USER
51 #include <ndk/ntndk.h>
52
53 /* FMIFS Public Header */
54 #include <fmifs/fmifs.h>
55
56 #define FMIFS_IMPORT_DLL
57
58 //
59 // Globals
60 //
61 BOOL Error = FALSE;
62
63 // switches
64 BOOL FixErrors = FALSE;
65 BOOL SkipClean = FALSE;
66 BOOL ScanSectors = FALSE;
67 BOOL Verbose = FALSE;
68 PWCHAR Drive = NULL;
69 WCHAR CurrentDirectory[1024];
70
71 #ifndef FMIFS_IMPORT_DLL
72 //
73 // FMIFS function
74 //
75 // PCHKDSK Chkdsk;
76 #endif /* ndef FMIFS_IMPORT_DLL */
77
78
79 //----------------------------------------------------------------------
80 //
81 // PrintWin32Error
82 //
83 // Takes the win32 error code and prints the text version.
84 //
85 //----------------------------------------------------------------------
86 static VOID PrintWin32Error(LPWSTR Message, DWORD ErrorCode)
87 {
88 LPWSTR lpMsgBuf;
89
90 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
91 NULL, ErrorCode,
92 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
93 (LPWSTR)&lpMsgBuf, 0, NULL);
94
95 wprintf(L"%s: %s\n", Message, lpMsgBuf);
96 LocalFree(lpMsgBuf);
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 wprintf(L"Usage: %s [drive:] [-F] [-V] [-R] [-C]\n\n"
130 L"[drive:] Specifies the drive to check.\n"
131 L"-F Fixes errors on the disk.\n"
132 L"-V Displays the full path of every file on the disk.\n"
133 L"-R Locates bad sectors and recovers readable information.\n"
134 L"-C Checks the drive only if it is dirty.\n\n",
135 ProgramName);
136 }
137
138
139 //----------------------------------------------------------------------
140 //
141 // ParseCommandLine
142 //
143 // Get the switches.
144 //
145 //----------------------------------------------------------------------
146 static int
147 ParseCommandLine(
148 int argc,
149 WCHAR *argv[]
150 )
151 {
152 int i;
153 BOOLEAN gotFix = FALSE;
154 BOOLEAN gotVerbose = FALSE;
155 BOOLEAN gotClean = FALSE;
156 // BOOLEAN gotScan = FALSE;
157
158 for (i = 1; i < argc; i++)
159 {
160 switch (argv[i][0])
161 {
162 case L'-': case L'/':
163
164 switch (argv[i][1])
165 {
166 // case L'?':
167 // Usage(argv[0]);
168 // return i;
169
170 case L'F': case L'f':
171 {
172 if (gotFix) return i;
173 FixErrors = TRUE;
174 gotFix = TRUE;
175 break;
176 }
177
178 case L'V': case L'v':
179 {
180 if (gotVerbose) return i;
181 Verbose = TRUE;
182 gotVerbose = TRUE;
183 break;
184 }
185
186 case L'R': case L'r':
187 {
188 if (gotFix) return i;
189 ScanSectors = TRUE;
190 gotFix = TRUE;
191 break;
192 }
193
194 case L'C': case L'c':
195 {
196 if (gotClean) return i;
197 SkipClean = TRUE;
198 gotClean = TRUE;
199 break;
200 }
201
202 default:
203 return i;
204 }
205 break;
206
207 default:
208 {
209 if (Drive) return i;
210 if (argv[i][1] != L':') return i;
211
212 Drive = argv[i];
213 break;
214 }
215 }
216 }
217 return 0;
218 }
219
220
221 //----------------------------------------------------------------------
222 //
223 // ChkdskCallback
224 //
225 // The file system library will call us back with commands that we
226 // can interpret. If we wanted to halt the chkdsk we could return FALSE.
227 //
228 //----------------------------------------------------------------------
229 BOOLEAN
230 WINAPI
231 ChkdskCallback(
232 CALLBACKCOMMAND Command,
233 DWORD Modifier,
234 PVOID Argument
235 )
236 {
237 PDWORD percent;
238 PBOOLEAN status;
239 PTEXTOUTPUT output;
240
241 //
242 // We get other types of commands,
243 // but we don't have to pay attention to them
244 //
245 switch (Command)
246 {
247 case UNKNOWN2:
248 wprintf(L"UNKNOWN2\r");
249 break;
250
251 case UNKNOWN3:
252 wprintf(L"UNKNOWN3\n");
253 break;
254
255 case UNKNOWN4:
256 wprintf(L"UNKNOWN4\n");
257 break;
258
259 case UNKNOWN5:
260 wprintf(L"UNKNOWN5\n");
261 break;
262
263 case FSNOTSUPPORTED:
264 wprintf(L"FSNOTSUPPORTED\n");
265 break;
266
267 case VOLUMEINUSE:
268 wprintf(L"VOLUMEINUSE\n");
269 break;
270
271 case UNKNOWN9:
272 wprintf(L"UNKNOWN9\n");
273 break;
274
275 case UNKNOWNA:
276 wprintf(L"UNKNOWNA\n");
277 break;
278
279 case UNKNOWNC:
280 wprintf(L"UNKNOWNC\n");
281 break;
282
283 case UNKNOWND:
284 wprintf(L"UNKNOWND\n");
285 break;
286
287 case INSUFFICIENTRIGHTS:
288 wprintf(L"INSUFFICIENTRIGHTS\n");
289 break;
290
291 case STRUCTUREPROGRESS:
292 wprintf(L"STRUCTUREPROGRESS\n");
293 break;
294
295 case DONEWITHSTRUCTURE:
296 wprintf(L"DONEWITHSTRUCTURE\n");
297 break;
298
299 case CLUSTERSIZETOOSMALL:
300 wprintf(L"CLUSTERSIZETOOSMALL\n");
301 break;
302
303 case PROGRESS:
304 percent = (PDWORD)Argument;
305 wprintf(L"%d percent completed.\r", *percent);
306 break;
307
308 case OUTPUT:
309 output = (PTEXTOUTPUT)Argument;
310 wprintf(L"%S", output->Output);
311 break;
312
313 case DONE:
314 status = (PBOOLEAN)Argument;
315 if (*status == FALSE)
316 {
317 wprintf(L"Chkdsk was unable to complete successfully.\n\n");
318 Error = TRUE;
319 }
320 break;
321 }
322 return TRUE;
323 }
324
325 #ifndef FMIFS_IMPORT_DLL
326 //----------------------------------------------------------------------
327 //
328 // LoadFMIFSEntryPoints
329 //
330 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
331 //
332 // 19990216 EA Used wide functions
333 //
334 //----------------------------------------------------------------------
335 static BOOLEAN
336 LoadFMIFSEntryPoints(VOID)
337 {
338 HMODULE hFmifs = LoadLibraryW(L"fmifs.dll");
339 if (hFmifs == NULL)
340 return FALSE;
341
342 Chkdsk = (PCHKDSK)GetProcAddress(hFmifs, "Chkdsk");
343
344 if (!Chkdsk)
345 {
346 FreeLibrary(hFmifs);
347 return FALSE;
348 }
349
350 return TRUE;
351 }
352 #endif /* ndef FMIFS_IMPORT_DLL */
353
354
355 //----------------------------------------------------------------------
356 //
357 // WMain
358 //
359 // Engine. Just get command line switches and fire off a chkdsk. This
360 // could also be done in a GUI like Explorer does when you select a
361 // drive and run a check on it.
362 //
363 // We do this in UNICODE because the chkdsk command expects PWCHAR
364 // arguments.
365 //
366 //----------------------------------------------------------------------
367 int
368 wmain(int argc, WCHAR *argv[])
369 {
370 int badArg;
371 HANDLE volumeHandle;
372 WCHAR fileSystem[1024];
373 WCHAR volumeName[1024];
374 DWORD serialNumber;
375 DWORD flags, maxComponent;
376
377 wprintf(L"\n\
378 Chkdskx v1.0.1 by Mark Russinovich\n\
379 Systems Internals - http://www.sysinternals.com/\n\
380 ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
381
382 #ifndef FMIFS_IMPORT_DLL
383 //
384 // Get function pointers
385 //
386 if (!LoadFMIFSEntryPoints())
387 {
388 wprintf(L"Could not located FMIFS entry points.\n\n");
389 return -1;
390 }
391 #endif /* ndef FMIFS_IMPORT_DLL */
392
393 //
394 // Parse command line
395 //
396 badArg = ParseCommandLine(argc, argv);
397 if (badArg)
398 {
399 wprintf(L"Unknown argument: %s\n", argv[badArg]);
400 Usage(argv[0]);
401 return -1;
402 }
403
404 //
405 // Get the drive's format
406 //
407 if (!Drive)
408 {
409 if (!GetCurrentDirectoryW(ARRAYSIZE(CurrentDirectory), CurrentDirectory))
410 {
411 PrintWin32Error(L"Could not get current directory", GetLastError());
412 return -1;
413 }
414 }
415 else
416 {
417 wcscpy(CurrentDirectory, Drive);
418 }
419 CurrentDirectory[2] = L'\\';
420 CurrentDirectory[3] = L'\0';
421 Drive = CurrentDirectory;
422
423 //
424 // Determine the drive's file system format, which we need to
425 // tell chkdsk
426 //
427 if (!GetVolumeInformationW(Drive,
428 volumeName,
429 ARRAYSIZE(volumeName),
430 &serialNumber,
431 &maxComponent,
432 &flags,
433 fileSystem,
434 ARRAYSIZE(fileSystem)))
435 {
436 PrintWin32Error(L"Could not query volume", GetLastError());
437 return -1;
438 }
439
440 //
441 // If they want to fix, we need to have access to the drive
442 //
443 if (FixErrors)
444 {
445 swprintf(volumeName, L"\\\\.\\%C:", Drive[0]);
446 volumeHandle = CreateFileW(volumeName,
447 GENERIC_WRITE,
448 0,
449 NULL,
450 OPEN_EXISTING,
451 0,
452 0);
453 if (volumeHandle == INVALID_HANDLE_VALUE)
454 {
455 wprintf(L"Chdskx cannot run because the volume is in use by another process.\n\n");
456 return -1;
457 }
458 CloseHandle(volumeHandle);
459
460 //
461 // Can't let the user break out of a chkdsk that can modify the drive
462 //
463 SetConsoleCtrlHandler(CtrlCIntercept, TRUE);
464 }
465
466 //
467 // Just do it
468 //
469 wprintf(L"The type of file system is %s.\n", fileSystem);
470 Chkdsk(Drive,
471 fileSystem,
472 FixErrors,
473 Verbose,
474 SkipClean,
475 ScanSectors,
476 NULL,
477 NULL,
478 ChkdskCallback);
479
480 if (Error) return -1;
481 return 0;
482 }
483
484 /* EOF */