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