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
12 /* INCLUDES *****************************************************************/
15 #define WIN32_NO_STATUS
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>
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>
39 /* DEFINES ******************************************************************/
41 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
43 typedef struct _FILESYSTEM_CHKDSK
47 } FILESYSTEM_CHKDSK
, *PFILESYSTEM_CHKDSK
;
49 FILESYSTEM_CHKDSK FileSystems
[10] =
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
},
63 HANDLE KeyboardHandle
;
65 /* FUNCTIONS ****************************************************************/
71 PrintString(char* fmt
,...)
75 UNICODE_STRING UnicodeString
;
76 ANSI_STRING AnsiString
;
79 vsprintf(buffer
, fmt
, ap
);
82 RtlInitAnsiString(&AnsiString
, buffer
);
83 RtlAnsiStringToUnicodeString(&UnicodeString
,
86 NtDisplayString(&UnicodeString
);
87 RtlFreeUnicodeString(&UnicodeString
);
90 // this func is taken from kernel32/file/volume.c
96 UNICODE_STRING NtPathU
;
97 OBJECT_ATTRIBUTES ObjectAttributes
;
99 IO_STATUS_BLOCK IoStatusBlock
;
102 if (!RtlDosPathNameToNtPathName_U(DirName
,
107 DPRINT1("Invalid path!\n");
108 return INVALID_HANDLE_VALUE
;
111 InitializeObjectAttributes(
114 OBJ_CASE_INSENSITIVE
,
118 Status
= NtCreateFile(
120 Write
? FILE_GENERIC_WRITE
: FILE_GENERIC_READ
,
125 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
131 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU
.Buffer
);
133 if (!NT_SUCCESS(Status
))
135 return INVALID_HANDLE_VALUE
;
144 OBJECT_ATTRIBUTES ObjectAttributes
;
145 IO_STATUS_BLOCK IoStatusBlock
;
146 UNICODE_STRING KeyboardName
= RTL_CONSTANT_STRING(L
"\\Device\\KeyboardClass0");
148 /* Just open the class driver */
149 InitializeObjectAttributes(&ObjectAttributes
,
154 return NtOpenFile(&KeyboardHandle
,
167 IO_STATUS_BLOCK IoStatusBlock
;
168 LARGE_INTEGER Offset
, Timeout
;
169 KEYBOARD_INPUT_DATA InputData
;
171 /* Attempt to read from the keyboard */
173 Status
= NtReadFile(KeyboardHandle
,
179 sizeof(KEYBOARD_INPUT_DATA
),
182 if (Status
== STATUS_PENDING
)
184 /* Wait TimeOut seconds */
185 Timeout
.QuadPart
= TimeOut
* -10000000;
186 Status
= NtWaitForSingleObject(KeyboardHandle
, FALSE
, &Timeout
);
187 /* The user didn't enter anything, cancel the read */
188 if (Status
== STATUS_TIMEOUT
)
190 NtCancelIoFile(KeyboardHandle
, &IoStatusBlock
);
192 /* Else, return some status */
195 Status
= IoStatusBlock
.Status
;
205 IN OUT LPWSTR FileSystemName
,
206 IN SIZE_T FileSystemNameSize
)
210 IO_STATUS_BLOCK IoStatusBlock
;
211 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute
;
212 UCHAR Buffer
[FS_ATTRIBUTE_BUFFER_SIZE
];
214 FileFsAttribute
= (PFILE_FS_ATTRIBUTE_INFORMATION
)Buffer
;
216 FileHandle
= OpenDirectory(Drive
, FALSE
);
217 if (FileHandle
== INVALID_HANDLE_VALUE
)
218 return STATUS_INVALID_PARAMETER
;
220 Status
= NtQueryVolumeInformationFile(FileHandle
,
223 FS_ATTRIBUTE_BUFFER_SIZE
,
224 FileFsAttributeInformation
);
227 if (NT_SUCCESS(Status
))
229 if (FileSystemNameSize
* sizeof(WCHAR
) >= FileFsAttribute
->FileSystemNameLength
+ sizeof(WCHAR
))
231 CopyMemory(FileSystemName
,
232 FileFsAttribute
->FileSystemName
,
233 FileFsAttribute
->FileSystemNameLength
);
234 FileSystemName
[FileFsAttribute
->FileSystemNameLength
/ sizeof(WCHAR
)] = 0;
237 return STATUS_BUFFER_TOO_SMALL
;
242 return STATUS_SUCCESS
;
245 // This is based on SysInternal's ChkDsk app
248 IN CALLBACKCOMMAND Command
,
257 // We get other types of commands,
258 // but we don't have to pay attention to them
263 DPRINT("UNKNOWN2\n");
267 DPRINT("UNKNOWN3\n");
271 DPRINT("UNKNOWN4\n");
275 DPRINT("UNKNOWN5\n");
279 DPRINT("UNKNOWN9\n");
283 DPRINT("UNKNOWNA\n");
287 DPRINT("UNKNOWNC\n");
291 DPRINT("UNKNOWND\n");
294 case INSUFFICIENTRIGHTS
:
295 DPRINT("INSUFFICIENTRIGHTS\n");
299 DPRINT("FSNOTSUPPORTED\n");
303 DPRINT("VOLUMEINUSE\n");
306 case STRUCTUREPROGRESS
:
307 DPRINT("STRUCTUREPROGRESS\n");
310 case DONEWITHSTRUCTURE
:
311 DPRINT("DONEWITHSTRUCTURE\n");
314 case CLUSTERSIZETOOSMALL
:
315 DPRINT("CLUSTERSIZETOOSMALL\n");
319 Percent
= (PDWORD
) Argument
;
320 PrintString("%d percent completed.\r", *Percent
);
324 Output
= (PTEXTOUTPUT
) Argument
;
325 PrintString("%s", Output
->Output
);
329 Status
= (PBOOLEAN
)Argument
;
330 if (*Status
!= FALSE
)
332 PrintString("Autochk was unable to complete successfully.\r\n\r\n");
345 WCHAR FileSystem
[128];
346 WCHAR NtDrivePath
[64];
347 UNICODE_STRING DrivePathU
;
351 PrintString(" Verifying volume %S\r\n", DrivePath
);
353 /* Get the file system */
354 Status
= GetFileSystem(DrivePath
,
356 ARRAYSIZE(FileSystem
));
357 if (!NT_SUCCESS(Status
))
359 DPRINT1("GetFileSystem() failed with status 0x%08lx\n", Status
);
360 PrintString(" Unable to get file system of %S\r\n", DrivePath
);
364 PrintString(" Its filesystem type is %S\r\n", FileSystem
);
367 for (Count
= 0; Count
< sizeof(FileSystems
) / sizeof(FileSystems
[0]); ++Count
)
369 if (wcscmp(FileSystem
, FileSystems
[Count
].Name
) != 0)
374 swprintf(NtDrivePath
, L
"\\??\\");
375 wcscat(NtDrivePath
, DrivePath
);
376 NtDrivePath
[wcslen(NtDrivePath
)-1] = 0;
377 RtlInitUnicodeString(&DrivePathU
, NtDrivePath
);
379 DPRINT1("AUTOCHK: Checking %wZ\n", &DrivePathU
);
380 /* First, check whether the volume is dirty */
381 Status
= FileSystems
[Count
].ChkdskFunc(&DrivePathU
,
384 TRUE
, // CheckOnlyIfDirty
388 if (Status
== STATUS_DISK_CORRUPT_ERROR
)
392 /* Let the user decide whether to repair */
393 PrintString(" %S needs to be checked\r\n", DrivePath
);
394 PrintString(" You can skip it, but be advised it is not recommanded\r\n");
395 PrintString(" To skip disk checking press any key in %d second(s)\r\n", TimeOut
);
397 /* Timeout == fix it! */
398 WaitStatus
= WaitForKeyboard(TimeOut
);
399 if (WaitStatus
== STATUS_TIMEOUT
)
401 Status
= FileSystems
[Count
].ChkdskFunc(&DrivePathU
,
404 TRUE
, // CheckOnlyIfDirty
410 PrintString(" %S checking has been skipped\r\n", DrivePath
);
416 if (Count
== sizeof(FileSystems
) / sizeof(FileSystems
[0]))
418 DPRINT1("File system not supported\n");
419 PrintString(" Unable to verify a %S volume\r\n", FileSystem
);
420 return STATUS_DLL_NOT_FOUND
;
428 IN OUT PLONG TimeOut
)
430 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
432 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
433 QueryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
434 QueryTable
[0].Name
= L
"AutoChkTimeOut";
435 QueryTable
[0].EntryContext
= TimeOut
;
437 RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
, L
"Session Manager", QueryTable
, NULL
, NULL
);
438 /* See: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/autochk */
439 if (*TimeOut
> 259200)
443 else if (*TimeOut
< 0)
449 /* Native image's entry point */
457 PROCESS_DEVICEMAP_INFORMATION DeviceMap
;
460 WCHAR DrivePath
[128];
463 // Win2003 passes the only param - "*". Probably means to check all drives
465 DPRINT("Got %d params\n", argc);
466 for (i=0; i<argc; i++)
467 DPRINT("Param %d: %s\n", i, argv[i]);
472 QueryTimeout(&TimeOut
);
474 /* FIXME: We should probably use here the mount manager to be
475 * able to check volumes which don't have a drive letter.
478 Status
= NtQueryInformationProcess(NtCurrentProcess(),
481 sizeof(DeviceMap
.Query
),
483 if (!NT_SUCCESS(Status
))
485 DPRINT1("NtQueryInformationProcess() failed with status 0x%08lx\n",
491 Status
= OpenKeyboard();
492 if (!NT_SUCCESS(Status
))
494 DPRINT1("OpenKeyboard() failed with status 0x%08lx\n", Status
);
498 for (i
= 0; i
< 26; i
++)
500 if ((DeviceMap
.Query
.DriveMap
& (1 << i
))
501 && (DeviceMap
.Query
.DriveType
[i
] == DOSDEVICE_DRIVE_FIXED
))
503 swprintf(DrivePath
, L
"%c:\\", L
'A'+i
);
504 CheckVolume(DrivePath
, TimeOut
);
509 NtClose(KeyboardHandle
);
511 // PrintString(" Done\r\n\r\n");