Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[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 */
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 != FALSE)
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 if (wcscmp(FileSystem, L"RFSD") == 0)
279 {
280 RtlInitUnicodeString(&ProviderDll, L"ureiserfs.dll");
281 }
282 else if (wcscmp(FileSystem, L"FFS") == 0)
283 {
284 RtlInitUnicodeString(&ProviderDll, L"uffs.dll");
285 }
286 else if (wcscmp(FileSystem, L"CDFS") == 0)
287 {
288 RtlInitUnicodeString(&ProviderDll, L"ucdfs.dll");
289 }
290 else
291 {
292 return NULL;
293 }
294
295 Status = LdrLoadDll(NULL, NULL, &ProviderDll, &BaseAddress);
296 if (!NT_SUCCESS(Status))
297 return NULL;
298 return BaseAddress;
299 }
300
301 static NTSTATUS
302 CheckVolume(
303 IN PWCHAR DrivePath)
304 {
305 WCHAR FileSystem[128];
306 ANSI_STRING ChkdskFunctionName = RTL_CONSTANT_STRING("ChkdskEx");
307 PVOID Provider;
308 CHKDSKEX ChkdskFunc;
309 WCHAR NtDrivePath[64];
310 UNICODE_STRING DrivePathU;
311 NTSTATUS Status;
312
313 /* Get the file system */
314 Status = GetFileSystem(DrivePath,
315 FileSystem,
316 ARRAYSIZE(FileSystem));
317 if (!NT_SUCCESS(Status))
318 {
319 DPRINT1("GetFileSystem() failed with status 0x%08lx\n", Status);
320 PrintString(" Unable to get file system of %S\r\n", DrivePath);
321 return Status;
322 }
323
324 /* Load the provider which will do the chkdsk */
325 Provider = LoadProvider(FileSystem);
326 if (Provider == NULL)
327 {
328 DPRINT1("LoadProvider() failed\n");
329 PrintString(" Unable to verify a %S volume\r\n", FileSystem);
330 return STATUS_DLL_NOT_FOUND;
331 }
332
333 /* Get the Chkdsk function address */
334 Status = LdrGetProcedureAddress(Provider,
335 &ChkdskFunctionName,
336 0,
337 (PVOID*)&ChkdskFunc);
338 if (!NT_SUCCESS(Status))
339 {
340 DPRINT1("LdrGetProcedureAddress() failed with status 0x%08lx\n", Status);
341 PrintString(" Unable to verify a %S volume\r\n", FileSystem);
342 LdrUnloadDll(Provider);
343 return Status;
344 }
345
346 /* Call provider */
347 // PrintString(" Verifying volume %S\r\n", DrivePath);
348 swprintf(NtDrivePath, L"\\??\\");
349 wcscat(NtDrivePath, DrivePath);
350 NtDrivePath[wcslen(NtDrivePath)-1] = 0;
351 RtlInitUnicodeString(&DrivePathU, NtDrivePath);
352
353 DPRINT1("AUTOCHK: Checking %wZ\n", &DrivePathU);
354 Status = ChkdskFunc(&DrivePathU,
355 TRUE, // FixErrors
356 TRUE, // Verbose
357 TRUE, // CheckOnlyIfDirty
358 FALSE,// ScanDrive
359 ChkdskCallback);
360
361 LdrUnloadDll(Provider);
362 return Status;
363 }
364
365 /* Native image's entry point */
366 int
367 _cdecl
368 _main(int argc,
369 char *argv[],
370 char *envp[],
371 int DebugFlag)
372 {
373 PROCESS_DEVICEMAP_INFORMATION DeviceMap;
374 ULONG i;
375 NTSTATUS Status;
376 WCHAR DrivePath[128];
377
378 // Win2003 passes the only param - "*". Probably means to check all drives
379 /*
380 DPRINT("Got %d params\n", argc);
381 for (i=0; i<argc; i++)
382 DPRINT("Param %d: %s\n", i, argv[i]);
383 */
384
385 /* FIXME: We should probably use here the mount manager to be
386 * able to check volumes which don't have a drive letter.
387 */
388
389 Status = NtQueryInformationProcess(NtCurrentProcess(),
390 ProcessDeviceMap,
391 &DeviceMap.Query,
392 sizeof(DeviceMap.Query),
393 NULL);
394 if (!NT_SUCCESS(Status))
395 {
396 DPRINT1("NtQueryInformationProcess() failed with status 0x%08lx\n",
397 Status);
398 return 1;
399 }
400
401 for (i = 0; i < 26; i++)
402 {
403 if ((DeviceMap.Query.DriveMap & (1 << i))
404 && (DeviceMap.Query.DriveType[i] == DOSDEVICE_DRIVE_FIXED))
405 {
406 swprintf(DrivePath, L"%c:\\", L'A'+i);
407 CheckVolume(DrivePath);
408 }
409 }
410 // PrintString(" Done\r\n\r\n");
411 return 0;
412 }
413
414 /* EOF */