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