[AUTOCHK]
[reactos.git] / reactos / 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 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <stdio.h>
14 #define WIN32_NO_STATUS
15 #include <windef.h>
16 #include <winbase.h>
17 #define NTOS_MODE_USER
18 #include <ndk/exfuncs.h>
19 #include <ndk/iofuncs.h>
20 #include <ndk/obfuncs.h>
21 #include <ndk/psfuncs.h>
22 #include <ndk/rtlfuncs.h>
23 #include <ndk/umfuncs.h>
24 #include <fmifs/fmifs.h>
25
26 #define NDEBUG
27 #include <debug.h>
28
29 /* DEFINES ******************************************************************/
30
31 #define FS_ATTRIBUTE_BUFFER_SIZE (MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
32
33
34 /* FUNCTIONS ****************************************************************/
35 //
36 // FMIFS function
37 //
38
39 static VOID
40 PrintString(char* fmt,...)
41 {
42 char buffer[512];
43 va_list ap;
44 UNICODE_STRING UnicodeString;
45 ANSI_STRING AnsiString;
46
47 va_start(ap, fmt);
48 vsprintf(buffer, fmt, ap);
49 va_end(ap);
50
51 RtlInitAnsiString(&AnsiString, buffer);
52 RtlAnsiStringToUnicodeString(&UnicodeString,
53 &AnsiString,
54 TRUE);
55 NtDisplayString(&UnicodeString);
56 RtlFreeUnicodeString(&UnicodeString);
57 }
58
59 // this func is taken from kernel32/file/volume.c
60 static HANDLE
61 OpenDirectory(
62 IN LPCWSTR DirName,
63 IN BOOLEAN Write)
64 {
65 UNICODE_STRING NtPathU;
66 OBJECT_ATTRIBUTES ObjectAttributes;
67 NTSTATUS Status;
68 IO_STATUS_BLOCK IoStatusBlock;
69 HANDLE hFile;
70
71 if (!RtlDosPathNameToNtPathName_U(DirName,
72 &NtPathU,
73 NULL,
74 NULL))
75 {
76 DPRINT1("Invalid path!\n");
77 return INVALID_HANDLE_VALUE;
78 }
79
80 InitializeObjectAttributes(
81 &ObjectAttributes,
82 &NtPathU,
83 OBJ_CASE_INSENSITIVE,
84 NULL,
85 NULL);
86
87 Status = NtCreateFile(
88 &hFile,
89 Write ? FILE_GENERIC_WRITE : FILE_GENERIC_READ,
90 &ObjectAttributes,
91 &IoStatusBlock,
92 NULL,
93 0,
94 FILE_SHARE_READ|FILE_SHARE_WRITE,
95 FILE_OPEN,
96 0,
97 NULL,
98 0);
99
100 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathU.Buffer);
101
102 if (!NT_SUCCESS(Status))
103 {
104 return INVALID_HANDLE_VALUE;
105 }
106
107 return hFile;
108 }
109
110 static NTSTATUS
111 GetFileSystem(
112 IN LPCWSTR Drive,
113 IN OUT LPWSTR FileSystemName,
114 IN SIZE_T FileSystemNameSize)
115 {
116 HANDLE FileHandle;
117 NTSTATUS Status;
118 IO_STATUS_BLOCK IoStatusBlock;
119 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute;
120 UCHAR Buffer[FS_ATTRIBUTE_BUFFER_SIZE];
121
122 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
123
124 FileHandle = OpenDirectory(Drive, FALSE);
125 if (FileHandle == INVALID_HANDLE_VALUE)
126 return STATUS_INVALID_PARAMETER;
127
128 Status = NtQueryVolumeInformationFile(FileHandle,
129 &IoStatusBlock,
130 FileFsAttribute,
131 FS_ATTRIBUTE_BUFFER_SIZE,
132 FileFsAttributeInformation);
133 NtClose(FileHandle);
134
135 if (NT_SUCCESS(Status))
136 {
137 if (FileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
138 {
139 CopyMemory(FileSystemName,
140 FileFsAttribute->FileSystemName,
141 FileFsAttribute->FileSystemNameLength);
142 FileSystemName[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0;
143 }
144 else
145 return STATUS_BUFFER_TOO_SMALL;
146 }
147 else
148 return Status;
149
150 return STATUS_SUCCESS;
151 }
152
153 // This is based on SysInternal's ChkDsk app
154 static BOOLEAN NTAPI
155 ChkdskCallback(
156 IN CALLBACKCOMMAND Command,
157 IN ULONG Modifier,
158 IN PVOID Argument)
159 {
160 PDWORD Percent;
161 PBOOLEAN Status;
162 PTEXTOUTPUT Output;
163
164 //
165 // We get other types of commands,
166 // but we don't have to pay attention to them
167 //
168 switch(Command)
169 {
170 case UNKNOWN2:
171 DPRINT("UNKNOWN2\n");
172 break;
173
174 case UNKNOWN3:
175 DPRINT("UNKNOWN3\n");
176 break;
177
178 case UNKNOWN4:
179 DPRINT("UNKNOWN4\n");
180 break;
181
182 case UNKNOWN5:
183 DPRINT("UNKNOWN5\n");
184 break;
185
186 case UNKNOWN9:
187 DPRINT("UNKNOWN9\n");
188 break;
189
190 case UNKNOWNA:
191 DPRINT("UNKNOWNA\n");
192 break;
193
194 case UNKNOWNC:
195 DPRINT("UNKNOWNC\n");
196 break;
197
198 case UNKNOWND:
199 DPRINT("UNKNOWND\n");
200 break;
201
202 case INSUFFICIENTRIGHTS:
203 DPRINT("INSUFFICIENTRIGHTS\n");
204 break;
205
206 case FSNOTSUPPORTED:
207 DPRINT("FSNOTSUPPORTED\n");
208 break;
209
210 case VOLUMEINUSE:
211 DPRINT("VOLUMEINUSE\n");
212 break;
213
214 case STRUCTUREPROGRESS:
215 DPRINT("STRUCTUREPROGRESS\n");
216 break;
217
218 case DONEWITHSTRUCTURE:
219 DPRINT("DONEWITHSTRUCTURE\n");
220 break;
221
222 case CLUSTERSIZETOOSMALL:
223 DPRINT("CLUSTERSIZETOOSMALL\n");
224 break;
225
226 case PROGRESS:
227 Percent = (PDWORD) Argument;
228 PrintString("%d percent completed.\r", *Percent);
229 break;
230
231 case OUTPUT:
232 Output = (PTEXTOUTPUT) Argument;
233 PrintString("%s", Output->Output);
234 break;
235
236 case DONE:
237 Status = (PBOOLEAN)Argument;
238 if (*Status == TRUE)
239 {
240 PrintString("Autochk was unable to complete successfully.\r\n\r\n");
241 // Error = TRUE;
242 }
243 break;
244 }
245 return TRUE;
246 }
247
248 /* Load the provider associated with this file system */
249 static PVOID
250 LoadProvider(
251 IN PWCHAR FileSystem)
252 {
253 UNICODE_STRING ProviderDll;
254 PVOID BaseAddress;
255 NTSTATUS Status;
256
257 /* FIXME: add more providers here */
258
259 if (wcscmp(FileSystem, L"NTFS") == 0)
260 {
261 RtlInitUnicodeString(&ProviderDll, L"untfs.dll");
262 }
263 else if (wcscmp(FileSystem, L"FAT") == 0
264 || wcscmp(FileSystem, L"FAT32") == 0)
265 {
266 RtlInitUnicodeString(&ProviderDll, L"ufat.dll");
267 }
268 else if (wcscmp(FileSystem, L"EXT2") == 0
269 || wcscmp(FileSystem, L"EXT3") == 0
270 || wcscmp(FileSystem, L"EXT4") == 0)
271 {
272 RtlInitUnicodeString(&ProviderDll, L"uext2.dll");
273 }
274 else if (wcscmp(FileSystem, L"Btrfs") == 0)
275 {
276 RtlInitUnicodeString(&ProviderDll, L"ubtrfs.dll");
277 }
278 else
279 {
280 return NULL;
281 }
282
283 Status = LdrLoadDll(NULL, NULL, &ProviderDll, &BaseAddress);
284 if (!NT_SUCCESS(Status))
285 return NULL;
286 return BaseAddress;
287 }
288
289 static NTSTATUS
290 CheckVolume(
291 IN PWCHAR DrivePath)
292 {
293 WCHAR FileSystem[128];
294 ANSI_STRING ChkdskFunctionName = RTL_CONSTANT_STRING("ChkdskEx");
295 PVOID Provider;
296 CHKDSKEX ChkdskFunc;
297 WCHAR NtDrivePath[64];
298 UNICODE_STRING DrivePathU;
299 NTSTATUS Status;
300
301 /* Get the file system */
302 Status = GetFileSystem(DrivePath,
303 FileSystem,
304 ARRAYSIZE(FileSystem));
305 if (!NT_SUCCESS(Status))
306 {
307 DPRINT1("GetFileSystem() failed with status 0x%08lx\n", Status);
308 PrintString(" Unable to get file system of %S\r\n", DrivePath);
309 return Status;
310 }
311
312 /* Load the provider which will do the chkdsk */
313 Provider = LoadProvider(FileSystem);
314 if (Provider == NULL)
315 {
316 DPRINT1("LoadProvider() failed\n");
317 PrintString(" Unable to verify a %S volume\r\n", FileSystem);
318 return STATUS_DLL_NOT_FOUND;
319 }
320
321 /* Get the Chkdsk function address */
322 Status = LdrGetProcedureAddress(Provider,
323 &ChkdskFunctionName,
324 0,
325 (PVOID*)&ChkdskFunc);
326 if (!NT_SUCCESS(Status))
327 {
328 DPRINT1("LdrGetProcedureAddress() failed with status 0x%08lx\n", Status);
329 PrintString(" Unable to verify a %S volume\r\n", FileSystem);
330 LdrUnloadDll(Provider);
331 return Status;
332 }
333
334 /* Call provider */
335 // PrintString(" Verifying volume %S\r\n", DrivePath);
336 swprintf(NtDrivePath, L"\\??\\");
337 wcscat(NtDrivePath, DrivePath);
338 NtDrivePath[wcslen(NtDrivePath)-1] = 0;
339 RtlInitUnicodeString(&DrivePathU, NtDrivePath);
340
341 DPRINT1("AUTOCHK: Checking %wZ\n", &DrivePathU);
342 Status = ChkdskFunc(&DrivePathU,
343 TRUE, // FixErrors
344 TRUE, // Verbose
345 TRUE, // CheckOnlyIfDirty
346 FALSE,// ScanDrive
347 ChkdskCallback);
348
349 LdrUnloadDll(Provider);
350 return Status;
351 }
352
353 /* Native image's entry point */
354 int
355 _cdecl
356 _main(int argc,
357 char *argv[],
358 char *envp[],
359 int DebugFlag)
360 {
361 PROCESS_DEVICEMAP_INFORMATION DeviceMap;
362 ULONG i;
363 NTSTATUS Status;
364 WCHAR DrivePath[128];
365
366 // Win2003 passes the only param - "*". Probably means to check all drives
367 /*
368 DPRINT("Got %d params\n", argc);
369 for (i=0; i<argc; i++)
370 DPRINT("Param %d: %s\n", i, argv[i]);
371 */
372
373 /* FIXME: We should probably use here the mount manager to be
374 * able to check volumes which don't have a drive letter.
375 */
376
377 Status = NtQueryInformationProcess(NtCurrentProcess(),
378 ProcessDeviceMap,
379 &DeviceMap.Query,
380 sizeof(DeviceMap.Query),
381 NULL);
382 if (!NT_SUCCESS(Status))
383 {
384 DPRINT1("NtQueryInformationProcess() failed with status 0x%08lx\n",
385 Status);
386 return 1;
387 }
388
389 for (i = 0; i < 26; i++)
390 {
391 if ((DeviceMap.Query.DriveMap & (1 << i))
392 && (DeviceMap.Query.DriveType[i] == DOSDEVICE_DRIVE_FIXED))
393 {
394 swprintf(DrivePath, L"%c:\\", L'A'+i);
395 CheckVolume(DrivePath);
396 }
397 }
398 // PrintString(" Done\r\n\r\n");
399 return 0;
400 }
401
402 /* EOF */