[KMTESTS:IO]
[reactos.git] / rostests / kmtests / ntos_io / IoFilesystem.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite File System test
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
6 */
7
8 #include <kmt_test.h>
9
10 /* FIXME: Test this stuff on non-FAT volumes */
11
12 static
13 VOID
14 Substitute(
15 _Out_writes_bytes_(BufferSize) PWCHAR Buffer,
16 _In_ ULONG BufferSize,
17 _In_ PCWSTR Template,
18 _In_ PCWSTR SystemDriveName,
19 _In_ PCWSTR SystemRootName)
20 {
21 UNICODE_STRING SystemDriveTemplate = RTL_CONSTANT_STRING(L"C:");
22 UNICODE_STRING SystemRootTemplate = RTL_CONSTANT_STRING(L"ReactOS");
23 ULONG SystemDriveLength;
24 ULONG SystemRootLength;
25 PWCHAR Dest = Buffer;
26 UNICODE_STRING String;
27
28 SystemDriveLength = wcslen(SystemDriveName) * sizeof(WCHAR);
29 SystemRootLength = wcslen(SystemRootName) * sizeof(WCHAR);
30
31 RtlInitUnicodeString(&String, Template);
32 ASSERT(String.Length % sizeof(WCHAR) == 0);
33 while (String.Length)
34 {
35 if (RtlPrefixUnicodeString(&SystemDriveTemplate, &String, TRUE))
36 {
37 ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemDriveLength < BufferSize);
38 RtlCopyMemory(Dest,
39 SystemDriveName,
40 SystemDriveLength);
41 Dest += SystemDriveLength / sizeof(WCHAR);
42
43 String.Buffer += SystemDriveTemplate.Length / sizeof(WCHAR);
44 String.Length -= SystemDriveTemplate.Length;
45 String.MaximumLength -= SystemDriveTemplate.Length;
46 continue;
47 }
48
49 if (RtlPrefixUnicodeString(&SystemRootTemplate, &String, TRUE))
50 {
51 ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemRootLength < BufferSize);
52 RtlCopyMemory(Dest,
53 SystemRootName,
54 SystemRootLength);
55 Dest += SystemRootLength / sizeof(WCHAR);
56
57 String.Buffer += SystemRootTemplate.Length / sizeof(WCHAR);
58 String.Length -= SystemRootTemplate.Length;
59 String.MaximumLength -= SystemRootTemplate.Length;
60 continue;
61 }
62
63 ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
64 *Dest++ = String.Buffer[0];
65
66 String.Buffer++;
67 String.Length -= sizeof(WCHAR);
68 String.MaximumLength -= sizeof(WCHAR);
69 }
70 ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
71 *Dest = UNICODE_NULL;
72 }
73
74 static
75 VOID
76 TestRelativeNames(VOID)
77 {
78 NTSTATUS Status;
79 struct
80 {
81 PCWSTR ParentPathTemplate;
82 PCWSTR RelativePathTemplate;
83 BOOLEAN IsDirectory;
84 NTSTATUS Status;
85 } Tests[] =
86 {
87 { NULL, L"C:\\", TRUE, STATUS_SUCCESS },
88 { NULL, L"C:\\\\", TRUE, STATUS_SUCCESS },
89 { NULL, L"C:\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
90 { NULL, L"C:\\ReactOS", TRUE, STATUS_SUCCESS },
91 { NULL, L"C:\\ReactOS\\", TRUE, STATUS_SUCCESS },
92 { NULL, L"C:\\ReactOS\\\\", TRUE, STATUS_SUCCESS },
93 { NULL, L"C:\\ReactOS\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
94 { NULL, L"C:\\\\ReactOS", TRUE, STATUS_SUCCESS },
95 { NULL, L"C:\\\\ReactOS\\", TRUE, STATUS_SUCCESS },
96 { NULL, L"C:\\ReactOS\\explorer.exe", FALSE, STATUS_SUCCESS },
97 { NULL, L"C:\\ReactOS\\\\explorer.exe", FALSE, STATUS_OBJECT_NAME_INVALID },
98 { NULL, L"C:\\ReactOS\\explorer.exe\\", FALSE, STATUS_OBJECT_NAME_INVALID },
99 { NULL, L"C:\\ReactOS\\explorer.exe\\\\", FALSE, STATUS_OBJECT_NAME_INVALID },
100 /* This will never return STATUS_NOT_A_DIRECTORY. IsDirectory=TRUE is a little hacky but achieves that without special handling */
101 { NULL, L"C:\\ReactOS\\explorer.exe\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
102 { L"C:\\", L"", TRUE, STATUS_SUCCESS },
103 { L"C:\\", L"\\", TRUE, STATUS_OBJECT_NAME_INVALID },
104 { L"C:\\", L"ReactOS", TRUE, STATUS_SUCCESS },
105 { L"C:\\", L"\\ReactOS", TRUE, STATUS_OBJECT_NAME_INVALID },
106 { L"C:\\", L"ReactOS\\", TRUE, STATUS_SUCCESS },
107 { L"C:\\", L"\\ReactOS\\", TRUE, STATUS_OBJECT_NAME_INVALID },
108 { L"C:\\ReactOS", L"", TRUE, STATUS_SUCCESS },
109 { L"C:\\ReactOS", L"explorer.exe", FALSE, STATUS_SUCCESS },
110 { L"C:\\ReactOS\\explorer.exe", L"", FALSE, STATUS_SUCCESS },
111 /* Let's try some nonexistent things */
112 { NULL, L"C:\\ReactOS\\IDoNotExist", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
113 { NULL, L"C:\\ReactOS\\IDoNotExist\\file", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
114 { NULL, L"C:\\ReactOS\\IDoNotExist\\file?", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
115 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
116 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
117 { NULL, L"C:\\ReactOS\\AmIInvalid?", FALSE, STATUS_OBJECT_NAME_INVALID },
118 { NULL, L"C:\\ReactOS\\.", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
119 { NULL, L"C:\\ReactOS\\..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
120 { NULL, L"C:\\ReactOS\\...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
121 { L"C:\\", L".", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
122 { L"C:\\", L"..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
123 { L"C:\\", L"...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
124 { L"C:\\ReactOS", L".", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
125 { L"C:\\ReactOS", L"..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
126 { L"C:\\ReactOS", L"...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
127 };
128 ULONG i;
129 OBJECT_ATTRIBUTES ObjectAttributes;
130 IO_STATUS_BLOCK IoStatus;
131 UNICODE_STRING ParentPath;
132 UNICODE_STRING RelativePath;
133 HANDLE ParentHandle;
134 HANDLE FileHandle;
135 UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
136 HANDLE SymbolicLinkHandle = NULL;
137 WCHAR LinkNameBuffer[128];
138 UNICODE_STRING SymbolicLinkName;
139 PWSTR SystemDriveName;
140 PWSTR SystemRootName;
141 PWCHAR Buffer = NULL;
142 BOOLEAN TrailingBackslash;
143
144 /* Query \SystemRoot */
145 InitializeObjectAttributes(&ObjectAttributes,
146 &SystemRoot,
147 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
148 NULL,
149 NULL);
150 Status = ZwOpenSymbolicLinkObject(&SymbolicLinkHandle,
151 GENERIC_READ,
152 &ObjectAttributes);
153 if (skip(NT_SUCCESS(Status), "Failed to open SystemRoot, %lx\n", Status))
154 return;
155
156 RtlInitEmptyUnicodeString(&SymbolicLinkName,
157 LinkNameBuffer,
158 sizeof(LinkNameBuffer));
159 Status = ZwQuerySymbolicLinkObject(SymbolicLinkHandle,
160 &SymbolicLinkName,
161 NULL);
162 ObCloseHandle(SymbolicLinkHandle, KernelMode);
163 if (skip(NT_SUCCESS(Status), "Failed to query SystemRoot, %lx\n", Status))
164 return;
165
166 /* Split SymbolicLinkName into drive and path */
167 SystemDriveName = SymbolicLinkName.Buffer;
168 SystemRootName = SymbolicLinkName.Buffer + SymbolicLinkName.Length / sizeof(WCHAR);
169 *SystemRootName-- = UNICODE_NULL;
170 while (*SystemRootName != L'\\')
171 {
172 ASSERT(SystemRootName > SymbolicLinkName.Buffer);
173 SystemRootName--;
174 }
175 *SystemRootName++ = UNICODE_NULL;
176 trace("System Drive: '%ls'\n", SystemDriveName);
177 trace("System Root: '%ls'\n", SystemRootName);
178
179 /* Allocate path buffer */
180 Buffer = ExAllocatePoolWithTag(PagedPool, MAXUSHORT, 'sFmK');
181 if (skip(Buffer != NULL, "No buffer\n"))
182 return;
183
184 /* Finally run some tests! */
185 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
186 {
187 /* Open parent directory first */
188 ParentHandle = NULL;
189 if (Tests[i].ParentPathTemplate)
190 {
191 Substitute(Buffer,
192 MAXUSHORT,
193 Tests[i].ParentPathTemplate,
194 SystemDriveName,
195 SystemRootName);
196 RtlInitUnicodeString(&ParentPath, Buffer);
197 InitializeObjectAttributes(&ObjectAttributes,
198 &ParentPath,
199 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
200 NULL,
201 NULL);
202 Status = ZwOpenFile(&ParentHandle,
203 GENERIC_READ,
204 &ObjectAttributes,
205 &IoStatus,
206 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
207 0);
208 ok(Status == STATUS_SUCCESS,
209 "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
210 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
211 continue;
212 }
213
214 /* Now open the relative file: */
215 Substitute(Buffer,
216 MAXUSHORT,
217 Tests[i].RelativePathTemplate,
218 SystemDriveName,
219 SystemRootName);
220 RtlInitUnicodeString(&RelativePath, Buffer);
221 InitializeObjectAttributes(&ObjectAttributes,
222 &RelativePath,
223 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
224 ParentHandle,
225 NULL);
226 TrailingBackslash = FALSE;
227 if (wcslen(Buffer) && Buffer[wcslen(Buffer) - 1] == L'\\')
228 TrailingBackslash = TRUE;
229
230 /* (1) No flags */
231 Status = ZwOpenFile(&FileHandle,
232 GENERIC_READ,
233 &ObjectAttributes,
234 &IoStatus,
235 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
236 0);
237 ok(Status == Tests[i].Status,
238 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
239 if (NT_SUCCESS(Status))
240 ObCloseHandle(FileHandle, KernelMode);
241
242 /* (2) Directory File */
243 Status = ZwOpenFile(&FileHandle,
244 GENERIC_READ,
245 &ObjectAttributes,
246 &IoStatus,
247 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
248 FILE_DIRECTORY_FILE);
249 if (Tests[i].IsDirectory || (!TrailingBackslash && !NT_SUCCESS(Tests[i].Status)))
250 ok(Status == Tests[i].Status,
251 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
252 else
253 ok(Status == STATUS_NOT_A_DIRECTORY,
254 "[%lu] Status = %lx, expected STATUS_NOT_A_DIRECTORY\n", i, Status);
255 if (NT_SUCCESS(Status))
256 ObCloseHandle(FileHandle, KernelMode);
257
258 /* (3) Non-Directory File */
259 Status = ZwOpenFile(&FileHandle,
260 GENERIC_READ,
261 &ObjectAttributes,
262 &IoStatus,
263 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
264 FILE_NON_DIRECTORY_FILE);
265 if (Tests[i].IsDirectory && NT_SUCCESS(Tests[i].Status))
266 ok(Status == STATUS_FILE_IS_A_DIRECTORY,
267 "[%lu] Status = %lx, expected STATUS_FILE_IS_A_DIRECTORY\n", i, Status);
268 else
269 ok(Status == Tests[i].Status,
270 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
271 if (NT_SUCCESS(Status))
272 ObCloseHandle(FileHandle, KernelMode);
273
274 /* And close */
275 ObCloseHandle(ParentHandle, KernelMode);
276 }
277
278 ExFreePoolWithTag(Buffer, 'sFmK');
279 }
280
281 static
282 VOID
283 TestSharedCacheMap(VOID)
284 {
285 NTSTATUS Status;
286 struct
287 {
288 PCWSTR ParentPath;
289 PCWSTR RelativePath;
290 } Tests[] =
291 {
292 { 0, L"\\SystemRoot\\system32\\drivers\\etc\\hosts" },
293 { L"\\SystemRoot", L"system32\\drivers\\etc\\hosts" },
294 { L"\\SystemRoot\\system32", L"drivers\\etc\\hosts" },
295 { L"\\SystemRoot\\system32\\drivers", L"etc\\hosts" },
296 { L"\\SystemRoot\\system32\\drivers\\etc", L"hosts" },
297 };
298 OBJECT_ATTRIBUTES ObjectAttributes;
299 IO_STATUS_BLOCK IoStatus;
300 UNICODE_STRING ParentPath;
301 UNICODE_STRING RelativePath;
302 HANDLE ParentHandle[RTL_NUMBER_OF(Tests)] = { NULL };
303 HANDLE FileHandle[RTL_NUMBER_OF(Tests)] = { NULL };
304 PFILE_OBJECT FileObject[RTL_NUMBER_OF(Tests)] = { NULL };
305 PFILE_OBJECT SystemRootObject = NULL;
306 UCHAR Buffer[32];
307 HANDLE EventHandle;
308 LARGE_INTEGER FileOffset;
309 ULONG i;
310
311 /* We need an event for ZwReadFile */
312 InitializeObjectAttributes(&ObjectAttributes,
313 NULL,
314 OBJ_KERNEL_HANDLE,
315 NULL,
316 NULL);
317 Status = ZwCreateEvent(&EventHandle,
318 SYNCHRONIZE,
319 &ObjectAttributes,
320 NotificationEvent,
321 FALSE);
322 if (skip(NT_SUCCESS(Status), "No event\n"))
323 goto Cleanup;
324
325 /* Open all test files and get their FILE_OBJECT pointers */
326 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
327 {
328 if (Tests[i].ParentPath)
329 {
330 RtlInitUnicodeString(&ParentPath, Tests[i].ParentPath);
331 InitializeObjectAttributes(&ObjectAttributes,
332 &ParentPath,
333 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
334 NULL,
335 NULL);
336 Status = ZwOpenFile(&ParentHandle[i],
337 GENERIC_READ,
338 &ObjectAttributes,
339 &IoStatus,
340 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
341 0);
342 ok_eq_hex(Status, STATUS_SUCCESS);
343 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
344 goto Cleanup;
345 }
346
347 RtlInitUnicodeString(&RelativePath, Tests[i].RelativePath);
348 InitializeObjectAttributes(&ObjectAttributes,
349 &RelativePath,
350 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
351 ParentHandle[i],
352 NULL);
353 Status = ZwOpenFile(&FileHandle[i],
354 FILE_ALL_ACCESS,
355 &ObjectAttributes,
356 &IoStatus,
357 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
358 0);
359 ok_eq_hex(Status, STATUS_SUCCESS);
360 if (skip(NT_SUCCESS(Status), "No file handle %lu\n", i))
361 goto Cleanup;
362
363 Status = ObReferenceObjectByHandle(FileHandle[i],
364 FILE_ALL_ACCESS,
365 *IoFileObjectType,
366 KernelMode,
367 (PVOID*)&FileObject[i],
368 NULL);
369 ok_eq_hex(Status, STATUS_SUCCESS);
370 if (skip(NT_SUCCESS(Status), "No file object %lu\n", i))
371 goto Cleanup;
372 }
373
374 /* Also get a file object for the SystemRoot directory */
375 Status = ObReferenceObjectByHandle(ParentHandle[1],
376 GENERIC_READ,
377 *IoFileObjectType,
378 KernelMode,
379 (PVOID*)&SystemRootObject,
380 NULL);
381 ok_eq_hex(Status, STATUS_SUCCESS);
382 if (skip(NT_SUCCESS(Status), "No SystemRoot object\n"))
383 goto Cleanup;
384
385 /* Before read, caching is not initialized */
386 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
387 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
388 {
389 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
390 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
391 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
392 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
393 }
394 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
395 ok_eq_pointer(FileObject[0]->SectionObjectPointer->SharedCacheMap, NULL);
396
397 /* Perform a read on one handle to initialize caching */
398 FileOffset.QuadPart = 0;
399 Status = ZwReadFile(FileHandle[0],
400 EventHandle,
401 NULL,
402 NULL,
403 &IoStatus,
404 Buffer,
405 sizeof(Buffer),
406 &FileOffset,
407 NULL);
408 if (Status == STATUS_PENDING)
409 {
410 Status = ZwWaitForSingleObject(EventHandle, FALSE, NULL);
411 ok_eq_hex(Status, STATUS_SUCCESS);
412 Status = IoStatus.Status;
413 }
414 ok_eq_hex(Status, STATUS_SUCCESS);
415
416 /* Now we see a SharedCacheMap for the file */
417 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
418 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
419 {
420 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
421 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
422 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
423 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
424 }
425 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
426 ok(FileObject[0]->SectionObjectPointer->SharedCacheMap != NULL, "SharedCacheMap is NULL\n");
427
428 Cleanup:
429 if (SystemRootObject)
430 ObDereferenceObject(SystemRootObject);
431 if (EventHandle)
432 ObCloseHandle(EventHandle, KernelMode);
433 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
434 {
435 if (FileObject[i])
436 ObDereferenceObject(FileObject[i]);
437 if (FileHandle[i])
438 ObCloseHandle(FileHandle[i], KernelMode);
439 if (ParentHandle[i])
440 ObCloseHandle(ParentHandle[i], KernelMode);
441 }
442 }
443
444 START_TEST(IoFilesystem)
445 {
446 TestRelativeNames();
447 TestSharedCacheMap();
448 }