[NtUser]
[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 // Functions in FMIFS.DLL
74 //
75 PCHKDSK Chkdsk;
76 #endif
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(int argc, WCHAR *argv[])
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 // return i;
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 PDWORD percent;
234 PBOOLEAN status;
235 PTEXTOUTPUT output;
236
237 //
238 // We get other types of commands,
239 // but we don't have to pay attention to them
240 //
241 switch (Command)
242 {
243 case UNKNOWN2:
244 wprintf(L"UNKNOWN2\r");
245 break;
246
247 case UNKNOWN3:
248 wprintf(L"UNKNOWN3\n");
249 break;
250
251 case UNKNOWN4:
252 wprintf(L"UNKNOWN4\n");
253 break;
254
255 case UNKNOWN5:
256 wprintf(L"UNKNOWN5\n");
257 break;
258
259 case FSNOTSUPPORTED:
260 wprintf(L"FSNOTSUPPORTED\n");
261 break;
262
263 case VOLUMEINUSE:
264 wprintf(L"VOLUMEINUSE\n");
265 break;
266
267 case UNKNOWN9:
268 wprintf(L"UNKNOWN9\n");
269 break;
270
271 case UNKNOWNA:
272 wprintf(L"UNKNOWNA\n");
273 break;
274
275 case UNKNOWNC:
276 wprintf(L"UNKNOWNC\n");
277 break;
278
279 case UNKNOWND:
280 wprintf(L"UNKNOWND\n");
281 break;
282
283 case INSUFFICIENTRIGHTS:
284 wprintf(L"INSUFFICIENTRIGHTS\n");
285 break;
286
287 case STRUCTUREPROGRESS:
288 wprintf(L"STRUCTUREPROGRESS\n");
289 break;
290
291 case DONEWITHSTRUCTURE:
292 wprintf(L"DONEWITHSTRUCTURE\n");
293 break;
294
295 case CLUSTERSIZETOOSMALL:
296 wprintf(L"CLUSTERSIZETOOSMALL\n");
297 break;
298
299 case PROGRESS:
300 percent = (PDWORD)Argument;
301 wprintf(L"%d percent completed.\r", *percent);
302 break;
303
304 case OUTPUT:
305 output = (PTEXTOUTPUT)Argument;
306 wprintf(L"%S", output->Output);
307 break;
308
309 case DONE:
310 status = (PBOOLEAN)Argument;
311 if (*status == FALSE)
312 {
313 wprintf(L"Chkdsk was unable to complete successfully.\n\n");
314 Error = TRUE;
315 }
316 break;
317 }
318 return TRUE;
319 }
320
321 #ifndef FMIFS_IMPORT_DLL
322 //----------------------------------------------------------------------
323 //
324 // LoadFMIFSEntryPoints
325 //
326 // Loads FMIFS.DLL and locates the entry point(s) we are going to use
327 //
328 //----------------------------------------------------------------------
329 static BOOLEAN
330 LoadFMIFSEntryPoints(VOID)
331 {
332 HMODULE hFmifs = LoadLibraryW(L"fmifs.dll");
333 if (hFmifs == NULL)
334 return FALSE;
335
336 Chkdsk = (PCHKDSK)GetProcAddress(hFmifs, "Chkdsk");
337 if (!Chkdsk)
338 {
339 FreeLibrary(hFmifs);
340 return FALSE;
341 }
342
343 return TRUE;
344 }
345 #endif
346
347
348 //----------------------------------------------------------------------
349 //
350 // WMain
351 //
352 // Engine. Just get command line switches and fire off a chkdsk. This
353 // could also be done in a GUI like Explorer does when you select a
354 // drive and run a check on it.
355 //
356 // We do this in UNICODE because the chkdsk command expects PWCHAR
357 // arguments.
358 //
359 //----------------------------------------------------------------------
360 int
361 wmain(int argc, WCHAR *argv[])
362 {
363 int badArg;
364 HANDLE volumeHandle;
365 WCHAR fileSystem[1024];
366 WCHAR volumeName[1024];
367 DWORD serialNumber;
368 DWORD flags, maxComponent;
369
370 wprintf(L"\n"
371 L"Chkdskx v1.0.1 by Mark Russinovich\n"
372 L"Systems Internals - http://www.sysinternals.com\n"
373 L"ReactOS adaptation 1999 by Emanuele Aliberti\n\n");
374
375 #ifndef FMIFS_IMPORT_DLL
376 //
377 // Get function pointers
378 //
379 if (!LoadFMIFSEntryPoints())
380 {
381 wprintf(L"Could not located FMIFS entry points.\n\n");
382 return -1;
383 }
384 #endif
385
386 //
387 // Parse command line
388 //
389 badArg = ParseCommandLine(argc, argv);
390 if (badArg)
391 {
392 wprintf(L"Unknown argument: %s\n", argv[badArg]);
393 Usage(argv[0]);
394 return -1;
395 }
396
397 //
398 // Get the drive's format
399 //
400 if (!Drive)
401 {
402 if (!GetCurrentDirectoryW(ARRAYSIZE(CurrentDirectory), CurrentDirectory))
403 {
404 PrintWin32Error(L"Could not get current directory", GetLastError());
405 return -1;
406 }
407 }
408 else
409 {
410 wcscpy(CurrentDirectory, Drive);
411 }
412 CurrentDirectory[2] = L'\\';
413 CurrentDirectory[3] = L'\0';
414 Drive = CurrentDirectory;
415
416 //
417 // Determine the drive's file system format, which we need to
418 // tell chkdsk
419 //
420 if (!GetVolumeInformationW(Drive,
421 volumeName,
422 ARRAYSIZE(volumeName),
423 &serialNumber,
424 &maxComponent,
425 &flags,
426 fileSystem,
427 ARRAYSIZE(fileSystem)))
428 {
429 PrintWin32Error(L"Could not query volume", GetLastError());
430 return -1;
431 }
432
433 //
434 // If they want to fix, we need to have access to the drive
435 //
436 if (FixErrors)
437 {
438 swprintf(volumeName, L"\\\\.\\%C:", Drive[0]);
439 volumeHandle = CreateFileW(volumeName,
440 GENERIC_WRITE,
441 0,
442 NULL,
443 OPEN_EXISTING,
444 0,
445 0);
446 if (volumeHandle == INVALID_HANDLE_VALUE)
447 {
448 wprintf(L"Chdskx cannot run because the volume is in use by another process.\n\n");
449 return -1;
450 }
451 CloseHandle(volumeHandle);
452
453 //
454 // Can't let the user break out of a chkdsk that can modify the drive
455 //
456 SetConsoleCtrlHandler(CtrlCIntercept, TRUE);
457 }
458
459 //
460 // Just do it
461 //
462 wprintf(L"The type of file system is %s.\n", fileSystem);
463 Chkdsk(Drive,
464 fileSystem,
465 FixErrors,
466 Verbose,
467 SkipClean,
468 ScanSectors,
469 NULL,
470 NULL,
471 ChkdskCallback);
472
473 if (Error) return -1;
474 return 0;
475 }
476
477 /* EOF */