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