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