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