918d709878a6411d7065fc99db0a76fe643b1d1a
[reactos.git] / base / system / autochk / autochk.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/autochk/autochk.c
5 * PURPOSE: Filesystem checker
6 * PROGRAMMERS: Aleksey Bragin
7 * Eric Kohl
8 * Hervé Poussineau
9 * Pierre Schweitzer
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <stdio.h>
15 #define WIN32_NO_STATUS
16 #include <windef.h>
17 #include <winbase.h>
18 #include <ntddkbd.h>
19 #define NTOS_MODE_USER
20 #include <ndk/exfuncs.h>
21 #include <ndk/iofuncs.h>
22 #include <ndk/obfuncs.h>
23 #include <ndk/psfuncs.h>
24 #include <ndk/rtlfuncs.h>
25 #include <ndk/umfuncs.h>
26 #include <fmifs/fmifs.h>
27
28 #include <fslib/vfatlib.h>
29 #include <fslib/ext2lib.h>
30 #include <fslib/ntfslib.h>
31 #include <fslib/cdfslib.h>
32 #include <fslib/btrfslib.h>
33 #include <fslib/ffslib.h>
34 #include <fslib/reiserfslib.h>
35
36 #define NDEBUG
37 #include <debug.h>
38
39 /* DEFINES ******************************************************************/
40
41 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
42
43 typedef struct _FILESYSTEM_CHKDSK
44 {
45 WCHAR Name[10];
46 CHKDSKEX ChkdskFunc;
47 } FILESYSTEM_CHKDSK, *PFILESYSTEM_CHKDSK;
48
49 FILESYSTEM_CHKDSK FileSystems[10] =
50 {
51 { L"FAT", VfatChkdsk },
52 { L"FAT32", VfatChkdsk },
53 { L"NTFS", NtfsChkdsk },
54 { L"EXT2", Ext2Chkdsk },
55 { L"EXT3", Ext2Chkdsk },
56 { L"EXT4", Ext2Chkdsk },
57 { L"Btrfs", BtrfsChkdskEx },
58 { L"RFSD", ReiserfsChkdsk },
59 { L"FFS", FfsChkdsk },
60 { L"CDFS", CdfsChkdsk },
61 };
62
63 HANDLE KeyboardHandle;
64
65 /* FUNCTIONS ****************************************************************/
66 //
67 // FMIFS function
68 //
69
70 static VOID
71 PrintString(char* fmt,...)
72 {
73 char buffer[512];
74 va_list ap;
75 UNICODE_STRING UnicodeString;
76 ANSI_STRING AnsiString;
77
78 va_start(ap, fmt);
79 vsprintf(buffer, fmt, ap);
80 va_end(ap);
81
82 RtlInitAnsiString(&AnsiString, buffer);
83 RtlAnsiStringToUnicodeString(&UnicodeString,
84 &AnsiString,
85 TRUE);
86 NtDisplayString(&UnicodeString);
87 RtlFreeUnicodeString(&UnicodeString);
88 }
89
90 // this func is taken from kernel32/file/volume.c
91 static HANDLE
92 OpenDirectory(
93 IN LPCWSTR DirName,
94 IN BOOLEAN Write)
95 {
96 UNICODE_STRING NtPathU;
97 OBJECT_ATTRIBUTES ObjectAttributes;
98 NTSTATUS Status;
99 IO_STATUS_BLOCK IoStatusBlock;
100 HANDLE hFile;
101
102 if (!RtlDosPathNameToNtPathName_U(DirName,
103 &NtPathU,
104 NULL,
105 NULL))
106 {
107 DPRINT1("Invalid path!\n");
108 return INVALID_HANDLE_VALUE;
109 }
110
111 InitializeObjectAttributes(
112 &ObjectAttributes,
113 &NtPathU,
114 OBJ_CASE_INSENSITIVE,
115 NULL,
116 NULL);
117
118 Status = NtCreateFile(
119 &hFile,
120 Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
121 &ObjectAttributes,
122 &IoStatusBlock,
123 NULL,
124 0,
125 FILE_SHARE_READ|FILE_SHARE_WRITE,
126 FILE_OPEN,
127 0,
128 NULL,
129 0);
130
131 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer);
132
133 if (!NT_SUCCESS(Status))
134 {
135 return INVALID_HANDLE_VALUE;
136 }
137
138 return hFile;
139 }
140
141 static NTSTATUS
142 OpenKeyboard(VOID)
143 {
144 OBJECT_ATTRIBUTES ObjectAttributes;
145 IO_STATUS_BLOCK IoStatusBlock;
146 UNICODE_STRING KeyboardName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
147
148 /* Just open the class driver */
149 InitializeObjectAttributes(&ObjectAttributes,
150 &KeyboardName,
151 0,
152 NULL,
153 NULL);
154 return NtOpenFile(&KeyboardHandle,
155 FILE_ALL_ACCESS,
156 &ObjectAttributes,
157 &IoStatusBlock,
158 FILE_OPEN,
159 0);
160 }
161
162 static NTSTATUS
163 WaitForKeyboard(VOID)
164 {
165 NTSTATUS Status;
166 IO_STATUS_BLOCK IoStatusBlock;
167 LARGE_INTEGER Offset, Timeout;
168 KEYBOARD_INPUT_DATA InputData;
169
170 /* Attempt to read from the keyboard */
171 Offset.QuadPart = 0;
172 Status = NtReadFile(KeyboardHandle,
173 NULL,
174 NULL,
175 NULL,
176 &IoStatusBlock,
177 &InputData,
178 sizeof(KEYBOARD_INPUT_DATA),
179 &Offset,
180 NULL);
181 if (Status == STATUS_PENDING)
182 {
183 /* Wait 3s */
184 Timeout.QuadPart = (LONG)-3*1000*1000*10;
185 Status = NtWaitForSingleObject(KeyboardHandle, FALSE, &Timeout);
186 /* The user didn't enter anything, cancel the read */
187 if (Status == STATUS_TIMEOUT)
188 {
189 NtCancelIoFile(KeyboardHandle, &IoStatusBlock);
190 }
191 /* Else, return some status */
192 else
193 {
194 Status = IoStatusBlock.Status;
195 }
196 }
197
198 return Status;
199 }
200
201 static NTSTATUS
202 GetFileSystem(
203 IN LPCWSTR Drive,
204 IN OUT LPWSTR FileSystemName,
205 IN SIZE_T FileSystemNameSize)
206 {
207 HANDLE FileHandle;
208 NTSTATUS Status;
209 IO_STATUS_BLOCK IoStatusBlock;
210 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
211 UCHAR Buffer[FS_ATTRIBUTE_BUFFER_SIZE];
212
213 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
214
215 FileHandle = OpenDirectory(Drive, FALSE);
216 if (FileHandle == INVALID_HANDLE_VALUE)
217 return STATUS_INVALID_PARAMETER;
218
219 Status = NtQueryVolumeInformationFile(FileHandle,
220 &IoStatusBlock,
221 FileFsAttribute,
222 FS_ATTRIBUTE_BUFFER_SIZE,
223 FileFsAttributeInformation);
224 NtClose(FileHandle);
225
226 if (NT_SUCCESS(Status))
227 {
228 if (FileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
229 {
230 CopyMemory(FileSystemName,
231 FileFsAttribute->FileSystemName,
232 FileFsAttribute->FileSystemNameLength);
233 FileSystemName[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
234 }
235 else
236 return STATUS_BUFFER_TOO_SMALL;
237 }
238 else
239 return Status;
240
241 return STATUS_SUCCESS;
242 }
243
244 // This is based on SysInternal's ChkDsk app
245 static BOOLEAN NTAPI
246 ChkdskCallback(
247 IN CALLBACKCOMMAND Command,
248 IN ULONG Modifier,
249 IN PVOID Argument)
250 {
251 PDWORD Percent;
252 PBOOLEAN Status;
253 PTEXTOUTPUT Output;
254
255 //
256 // We get other types of commands,
257 // but we don't have to pay attention to them
258 //
259 switch(Command)
260 {
261 case UNKNOWN2:
262 DPRINT("UNKNOWN2\n");
263 break;
264
265 case UNKNOWN3:
266 DPRINT("UNKNOWN3\n");
267 break;
268
269 case UNKNOWN4:
270 DPRINT("UNKNOWN4\n");
271 break;
272
273 case UNKNOWN5:
274 DPRINT("UNKNOWN5\n");
275 break;
276
277 case UNKNOWN9:
278 DPRINT("UNKNOWN9\n");
279 break;
280
281 case UNKNOWNA:
282 DPRINT("UNKNOWNA\n");
283 break;
284
285 case UNKNOWNC:
286 DPRINT("UNKNOWNC\n");
287 break;
288
289 case UNKNOWND:
290 DPRINT("UNKNOWND\n");
291 break;
292
293 case INSUFFICIENTRIGHTS:
294 DPRINT("INSUFFICIENTRIGHTS\n");
295 break;
296
297 case FSNOTSUPPORTED:
298 DPRINT("FSNOTSUPPORTED\n");
299 break;
300
301 case VOLUMEINUSE:
302 DPRINT("VOLUMEINUSE\n");
303 break;
304
305 case STRUCTUREPROGRESS:
306 DPRINT("STRUCTUREPROGRESS\n");
307 break;
308
309 case DONEWITHSTRUCTURE:
310 DPRINT("DONEWITHSTRUCTURE\n");
311 break;
312
313 case CLUSTERSIZETOOSMALL:
314 DPRINT("CLUSTERSIZETOOSMALL\n");
315 break;
316
317 case PROGRESS:
318 Percent = (PDWORD) Argument;
319 PrintString("%d percent completed.\r", *Percent);
320 break;
321
322 case OUTPUT:
323 Output = (PTEXTOUTPUT) Argument;
324 PrintString("%s", Output->Output);
325 break;
326
327 case DONE:
328 Status = (PBOOLEAN)Argument;
329 if (*Status != FALSE)
330 {
331 PrintString("Autochk was unable to complete successfully.\r\n\r\n");
332 // Error = TRUE;
333 }
334 break;
335 }
336 return TRUE;
337 }
338
339 static NTSTATUS
340 CheckVolume(
341 IN PWCHAR DrivePath)
342 {
343 WCHAR FileSystem[128];
344 WCHAR NtDrivePath[64];
345 UNICODE_STRING DrivePathU;
346 NTSTATUS Status;
347 DWORD Count;
348
349 PrintString(" Verifying volume %S\r\n", DrivePath);
350
351 /* Get the file system */
352 Status = GetFileSystem(DrivePath,
353 FileSystem,
354 ARRAYSIZE(FileSystem));
355 if (!NT_SUCCESS(Status))
356 {
357 DPRINT1("GetFileSystem() failed with status 0x%08lx\n", Status);
358 PrintString(" Unable to get file system of %S\r\n", DrivePath);
359 return Status;
360 }
361
362 PrintString(" Its filesystem type is %S\r\n", FileSystem);
363
364 /* Call provider */
365 for (Count = 0; Count < sizeof(FileSystems) / sizeof(FileSystems[0]); ++Count)
366 {
367 if (wcscmp(FileSystem, FileSystems[Count].Name) != 0)
368 {
369 continue;
370 }
371
372 swprintf(NtDrivePath, L"\\??\\");
373 wcscat(NtDrivePath, DrivePath);
374 NtDrivePath[wcslen(NtDrivePath)-1] = 0;
375 RtlInitUnicodeString(&DrivePathU, NtDrivePath);
376
377 DPRINT1("AUTOCHK: Checking %wZ\n", &DrivePathU);
378 /* First, check whether the volume is dirty */
379 Status = FileSystems[Count].ChkdskFunc(&DrivePathU,
380 FALSE, // FixErrors
381 TRUE, // Verbose
382 TRUE, // CheckOnlyIfDirty
383 FALSE,// ScanDrive
384 ChkdskCallback);
385 /* It is */
386 if (Status == STATUS_DISK_CORRUPT_ERROR)
387 {
388 NTSTATUS WaitStatus;
389
390 /* Let the user decide whether to repair */
391 PrintString(" %S needs to be checked\r\n", DrivePath);
392 PrintString(" You can skip it, but be advised it is not recommanded\r\n");
393 PrintString(" To skip disk checking press any key in 3 seconds\r\n", DrivePath);
394
395 /* Timeout == fix it! */
396 WaitStatus = WaitForKeyboard();
397 if (WaitStatus == STATUS_TIMEOUT)
398 {
399 Status = FileSystems[Count].ChkdskFunc(&DrivePathU,
400 TRUE, // FixErrors
401 TRUE, // Verbose
402 TRUE, // CheckOnlyIfDirty
403 FALSE,// ScanDrive
404 ChkdskCallback);
405 }
406 else
407 {
408 PrintString(" %S checking has been skipped\r\n", DrivePath);
409 }
410 }
411 break;
412 }
413
414 if (Count == sizeof(FileSystems) / sizeof(FileSystems[0]))
415 {
416 DPRINT1("File system not supported\n");
417 PrintString(" Unable to verify a %S volume\r\n", FileSystem);
418 return STATUS_DLL_NOT_FOUND;
419 }
420
421 return Status;
422 }
423
424 /* Native image's entry point */
425 int
426 _cdecl
427 _main(int argc,
428 char *argv[],
429 char *envp[],
430 int DebugFlag)
431 {
432 PROCESS_DEVICEMAP_INFORMATION DeviceMap;
433 ULONG i;
434 NTSTATUS Status;
435 WCHAR DrivePath[128];
436
437 // Win2003 passes the only param - "*". Probably means to check all drives
438 /*
439 DPRINT("Got %d params\n", argc);
440 for (i=0; i<argc; i++)
441 DPRINT("Param %d: %s\n", i, argv[i]);
442 */
443
444 /* FIXME: We should probably use here the mount manager to be
445 * able to check volumes which don't have a drive letter.
446 */
447
448 Status = NtQueryInformationProcess(NtCurrentProcess(),
449 ProcessDeviceMap,
450 &DeviceMap.Query,
451 sizeof(DeviceMap.Query),
452 NULL);
453 if (!NT_SUCCESS(Status))
454 {
455 DPRINT1("NtQueryInformationProcess() failed with status 0x%08lx\n",
456 Status);
457 return 1;
458 }
459
460 /* Open keyboard */
461 Status = OpenKeyboard();
462 if (!NT_SUCCESS(Status))
463 {
464 DPRINT1("OpenKeyboard() failed with status 0x%08lx\n", Status);
465 return 1;
466 }
467
468 for (i = 0; i < 26; i++)
469 {
470 if ((DeviceMap.Query.DriveMap & (1 << i))
471 && (DeviceMap.Query.DriveType[i] == DOSDEVICE_DRIVE_FIXED))
472 {
473 swprintf(DrivePath, L"%c:\\", L'A'+i);
474 CheckVolume(DrivePath);
475 }
476 }
477
478 /* Close keyboard */
479 NtClose(KeyboardHandle);
480
481 // PrintString(" Done\r\n\r\n");
482 return 0;
483 }
484
485 /* EOF */