[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 NTSTATUS
14 QueryFileInfo(
15 _In_ HANDLE FileHandle,
16 _Out_ PVOID *Info,
17 _Inout_ PSIZE_T Length,
18 _In_ FILE_INFORMATION_CLASS FileInformationClass)
19 {
20 NTSTATUS Status;
21 IO_STATUS_BLOCK IoStatus;
22 PVOID Buffer;
23
24 *Info = NULL;
25 if (*Length)
26 {
27 Buffer = KmtAllocateGuarded(*Length);
28 if (skip(Buffer != NULL, "Failed to allocate %Iu bytes\n", *Length))
29 return STATUS_INSUFFICIENT_RESOURCES;
30 }
31 else
32 {
33 Buffer = NULL;
34 }
35 RtlFillMemory(Buffer, *Length, 0xDD);
36 RtlFillMemory(&IoStatus, sizeof(IoStatus), 0x55);
37 _SEH2_TRY
38 {
39 Status = ZwQueryInformationFile(FileHandle,
40 &IoStatus,
41 Buffer,
42 *Length,
43 FileInformationClass);
44 }
45 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
46 {
47 Status = _SEH2_GetExceptionCode();
48 ok(0, "Exception %lx querying class %d with length %Iu\n",
49 Status, FileInformationClass, *Length);
50 }
51 _SEH2_END;
52 if (Status == STATUS_PENDING)
53 {
54 Status = ZwWaitForSingleObject(FileHandle, FALSE, NULL);
55 ok_eq_hex(Status, STATUS_SUCCESS);
56 Status = IoStatus.Status;
57 }
58 *Length = IoStatus.Information;
59 *Info = Buffer;
60 return Status;
61 }
62
63 static
64 VOID
65 TestAllInformation(VOID)
66 {
67 NTSTATUS Status;
68 UNICODE_STRING FileName = RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\ntoskrnl.exe");
69 UNICODE_STRING Ntoskrnl = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
70 OBJECT_ATTRIBUTES ObjectAttributes;
71 HANDLE FileHandle;
72 IO_STATUS_BLOCK IoStatus;
73 PFILE_ALL_INFORMATION FileAllInfo;
74 SIZE_T Length;
75 ULONG NameLength;
76 PWCHAR Name = NULL;
77 UNICODE_STRING NamePart;
78
79 InitializeObjectAttributes(&ObjectAttributes,
80 &FileName,
81 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
82 NULL,
83 NULL);
84 Status = ZwOpenFile(&FileHandle,
85 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
86 &ObjectAttributes,
87 &IoStatus,
88 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
89 FILE_NON_DIRECTORY_FILE);
90 if (Status == STATUS_PENDING)
91 {
92 Status = ZwWaitForSingleObject(FileHandle, FALSE, NULL);
93 ok_eq_hex(Status, STATUS_SUCCESS);
94 Status = IoStatus.Status;
95 }
96 ok_eq_hex(Status, STATUS_SUCCESS);
97 if (skip(NT_SUCCESS(Status), "No file handle, %lx\n", Status))
98 return;
99
100 /* NtQueryInformationFile doesn't do length checks for kernel callers in a free build */
101 if (KmtIsCheckedBuild)
102 {
103 /* Zero length */
104 Length = 0;
105 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
106 ok_eq_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
107 ok_eq_size(Length, (ULONG_PTR)0x5555555555555555);
108 if (FileAllInfo)
109 KmtFreeGuarded(FileAllInfo);
110
111 /* One less than the minimum */
112 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) - 1;
113 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
114 ok_eq_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
115 ok_eq_size(Length, (ULONG_PTR)0x5555555555555555);
116 if (FileAllInfo)
117 KmtFreeGuarded(FileAllInfo);
118 }
119
120 /* The minimum allowed */
121 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
122 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
123 ok_eq_hex(Status, STATUS_BUFFER_OVERFLOW);
124 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName));
125 if (FileAllInfo)
126 KmtFreeGuarded(FileAllInfo);
127
128 /* Plenty of space -- determine NameLength and copy the name */
129 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + MAX_PATH * sizeof(WCHAR);
130 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
131 ok_eq_hex(Status, STATUS_SUCCESS);
132 if (!skip(NT_SUCCESS(Status) && FileAllInfo != NULL, "No info\n"))
133 {
134 NameLength = FileAllInfo->NameInformation.FileNameLength;
135 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength);
136 Name = ExAllocatePoolWithTag(PagedPool, NameLength + sizeof(UNICODE_NULL), 'sFmK');
137 if (!skip(Name != NULL, "Could not allocate %lu bytes\n", NameLength + (ULONG)sizeof(UNICODE_NULL)))
138 {
139 RtlCopyMemory(Name,
140 FileAllInfo->NameInformation.FileName,
141 NameLength);
142 Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
143 ok(Name[0] == L'\\', "Name is %ls, expected first char to be \\\n", Name);
144 ok(NameLength >= Ntoskrnl.Length + sizeof(WCHAR), "NameLength %lu too short\n", NameLength);
145 if (NameLength >= Ntoskrnl.Length)
146 {
147 NamePart.Buffer = Name + (NameLength - Ntoskrnl.Length) / sizeof(WCHAR);
148 NamePart.Length = Ntoskrnl.Length;
149 NamePart.MaximumLength = NamePart.Length;
150 ok(RtlEqualUnicodeString(&NamePart, &Ntoskrnl, TRUE),
151 "Name ends in '%wZ', expected %wZ\n", &NamePart, &Ntoskrnl);
152 }
153 }
154 ok(FileAllInfo->NameInformation.FileName[NameLength / sizeof(WCHAR)] == 0xdddd,
155 "Char past FileName is %x\n",
156 FileAllInfo->NameInformation.FileName[NameLength / sizeof(WCHAR)]);
157 }
158 if (FileAllInfo)
159 KmtFreeGuarded(FileAllInfo);
160
161 /* One char less than needed */
162 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength - sizeof(WCHAR);
163 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
164 ok_eq_hex(Status, STATUS_BUFFER_OVERFLOW);
165 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength - sizeof(WCHAR));
166 if (FileAllInfo)
167 KmtFreeGuarded(FileAllInfo);
168
169 /* One byte less than needed */
170 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength - 1;
171 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
172 ok_eq_hex(Status, STATUS_BUFFER_OVERFLOW);
173 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength - 1);
174 if (FileAllInfo)
175 KmtFreeGuarded(FileAllInfo);
176
177 /* Exactly the required size */
178 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength;
179 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
180 ok_eq_hex(Status, STATUS_SUCCESS);
181 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength);
182 if (FileAllInfo)
183 KmtFreeGuarded(FileAllInfo);
184
185 /* One byte more than needed */
186 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength + 1;
187 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
188 ok_eq_hex(Status, STATUS_SUCCESS);
189 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength);
190 if (FileAllInfo)
191 KmtFreeGuarded(FileAllInfo);
192
193 /* One char more than needed */
194 Length = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength + sizeof(WCHAR);
195 Status = QueryFileInfo(FileHandle, (PVOID*)&FileAllInfo, &Length, FileAllInformation);
196 ok_eq_hex(Status, STATUS_SUCCESS);
197 ok_eq_size(Length, FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + NameLength);
198 if (FileAllInfo)
199 KmtFreeGuarded(FileAllInfo);
200
201 ExFreePoolWithTag(Name, 'sFmK');
202
203 Status = ObCloseHandle(FileHandle, KernelMode);
204 ok_eq_hex(Status, STATUS_SUCCESS);
205 }
206
207 static
208 VOID
209 Substitute(
210 _Out_writes_bytes_(BufferSize) PWCHAR Buffer,
211 _In_ ULONG BufferSize,
212 _In_ PCWSTR Template,
213 _In_ PCWSTR SystemDriveName,
214 _In_ PCWSTR SystemRootName)
215 {
216 UNICODE_STRING SystemDriveTemplate = RTL_CONSTANT_STRING(L"C:");
217 UNICODE_STRING SystemRootTemplate = RTL_CONSTANT_STRING(L"ReactOS");
218 ULONG SystemDriveLength;
219 ULONG SystemRootLength;
220 PWCHAR Dest = Buffer;
221 UNICODE_STRING String;
222
223 SystemDriveLength = wcslen(SystemDriveName) * sizeof(WCHAR);
224 SystemRootLength = wcslen(SystemRootName) * sizeof(WCHAR);
225
226 RtlInitUnicodeString(&String, Template);
227 ASSERT(String.Length % sizeof(WCHAR) == 0);
228 while (String.Length)
229 {
230 if (RtlPrefixUnicodeString(&SystemDriveTemplate, &String, TRUE))
231 {
232 ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemDriveLength < BufferSize);
233 RtlCopyMemory(Dest,
234 SystemDriveName,
235 SystemDriveLength);
236 Dest += SystemDriveLength / sizeof(WCHAR);
237
238 String.Buffer += SystemDriveTemplate.Length / sizeof(WCHAR);
239 String.Length -= SystemDriveTemplate.Length;
240 String.MaximumLength -= SystemDriveTemplate.Length;
241 continue;
242 }
243
244 if (RtlPrefixUnicodeString(&SystemRootTemplate, &String, TRUE))
245 {
246 ASSERT((Dest - Buffer) * sizeof(WCHAR) + SystemRootLength < BufferSize);
247 RtlCopyMemory(Dest,
248 SystemRootName,
249 SystemRootLength);
250 Dest += SystemRootLength / sizeof(WCHAR);
251
252 String.Buffer += SystemRootTemplate.Length / sizeof(WCHAR);
253 String.Length -= SystemRootTemplate.Length;
254 String.MaximumLength -= SystemRootTemplate.Length;
255 continue;
256 }
257
258 ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
259 *Dest++ = String.Buffer[0];
260
261 String.Buffer++;
262 String.Length -= sizeof(WCHAR);
263 String.MaximumLength -= sizeof(WCHAR);
264 }
265 ASSERT(Dest - Buffer < BufferSize / sizeof(WCHAR));
266 *Dest = UNICODE_NULL;
267 }
268
269 static
270 VOID
271 TestRelativeNames(VOID)
272 {
273 NTSTATUS Status;
274 struct
275 {
276 PCWSTR ParentPathTemplate;
277 PCWSTR RelativePathTemplate;
278 BOOLEAN IsDirectory;
279 NTSTATUS Status;
280 } Tests[] =
281 {
282 { NULL, L"C:\\", TRUE, STATUS_SUCCESS },
283 { NULL, L"C:\\\\", TRUE, STATUS_SUCCESS },
284 { NULL, L"C:\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
285 { NULL, L"C:\\ReactOS", TRUE, STATUS_SUCCESS },
286 { NULL, L"C:\\ReactOS\\", TRUE, STATUS_SUCCESS },
287 { NULL, L"C:\\ReactOS\\\\", TRUE, STATUS_SUCCESS },
288 { NULL, L"C:\\ReactOS\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
289 { NULL, L"C:\\\\ReactOS", TRUE, STATUS_SUCCESS },
290 { NULL, L"C:\\\\ReactOS\\", TRUE, STATUS_SUCCESS },
291 { NULL, L"C:\\ReactOS\\explorer.exe", FALSE, STATUS_SUCCESS },
292 { NULL, L"C:\\ReactOS\\\\explorer.exe", FALSE, STATUS_OBJECT_NAME_INVALID },
293 { NULL, L"C:\\ReactOS\\explorer.exe\\", FALSE, STATUS_OBJECT_NAME_INVALID },
294 { NULL, L"C:\\ReactOS\\explorer.exe\\\\", FALSE, STATUS_OBJECT_NAME_INVALID },
295 /* This will never return STATUS_NOT_A_DIRECTORY. IsDirectory=TRUE is a little hacky but achieves that without special handling */
296 { NULL, L"C:\\ReactOS\\explorer.exe\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
297 { L"C:\\", L"", TRUE, STATUS_SUCCESS },
298 { L"C:\\", L"\\", TRUE, STATUS_OBJECT_NAME_INVALID },
299 { L"C:\\", L"ReactOS", TRUE, STATUS_SUCCESS },
300 { L"C:\\", L"\\ReactOS", TRUE, STATUS_OBJECT_NAME_INVALID },
301 { L"C:\\", L"ReactOS\\", TRUE, STATUS_SUCCESS },
302 { L"C:\\", L"\\ReactOS\\", TRUE, STATUS_OBJECT_NAME_INVALID },
303 { L"C:\\ReactOS", L"", TRUE, STATUS_SUCCESS },
304 { L"C:\\ReactOS", L"explorer.exe", FALSE, STATUS_SUCCESS },
305 { L"C:\\ReactOS\\explorer.exe", L"", FALSE, STATUS_SUCCESS },
306 /* Let's try some nonexistent things */
307 { NULL, L"C:\\ReactOS\\IDoNotExist", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
308 { NULL, L"C:\\ReactOS\\IDoNotExist\\file", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
309 { NULL, L"C:\\ReactOS\\IDoNotExist\\file?", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
310 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
311 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
312 { NULL, L"C:\\ReactOS\\AmIInvalid?", FALSE, STATUS_OBJECT_NAME_INVALID },
313 { NULL, L"C:\\ReactOS\\.", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
314 { NULL, L"C:\\ReactOS\\..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
315 { NULL, L"C:\\ReactOS\\...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
316 { L"C:\\", L".", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
317 { L"C:\\", L"..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
318 { L"C:\\", L"...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
319 { L"C:\\ReactOS", L".", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
320 { L"C:\\ReactOS", L"..", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
321 { L"C:\\ReactOS", L"...", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
322 };
323 ULONG i;
324 OBJECT_ATTRIBUTES ObjectAttributes;
325 IO_STATUS_BLOCK IoStatus;
326 UNICODE_STRING ParentPath;
327 UNICODE_STRING RelativePath;
328 HANDLE ParentHandle;
329 HANDLE FileHandle;
330 UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
331 HANDLE SymbolicLinkHandle = NULL;
332 WCHAR LinkNameBuffer[128];
333 UNICODE_STRING SymbolicLinkName;
334 PWSTR SystemDriveName;
335 PWSTR SystemRootName;
336 PWCHAR Buffer = NULL;
337 BOOLEAN TrailingBackslash;
338
339 /* Query \SystemRoot */
340 InitializeObjectAttributes(&ObjectAttributes,
341 &SystemRoot,
342 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
343 NULL,
344 NULL);
345 Status = ZwOpenSymbolicLinkObject(&SymbolicLinkHandle,
346 GENERIC_READ,
347 &ObjectAttributes);
348 if (skip(NT_SUCCESS(Status), "Failed to open SystemRoot, %lx\n", Status))
349 return;
350
351 RtlInitEmptyUnicodeString(&SymbolicLinkName,
352 LinkNameBuffer,
353 sizeof(LinkNameBuffer));
354 Status = ZwQuerySymbolicLinkObject(SymbolicLinkHandle,
355 &SymbolicLinkName,
356 NULL);
357 ObCloseHandle(SymbolicLinkHandle, KernelMode);
358 if (skip(NT_SUCCESS(Status), "Failed to query SystemRoot, %lx\n", Status))
359 return;
360
361 /* Split SymbolicLinkName into drive and path */
362 SystemDriveName = SymbolicLinkName.Buffer;
363 SystemRootName = SymbolicLinkName.Buffer + SymbolicLinkName.Length / sizeof(WCHAR);
364 *SystemRootName-- = UNICODE_NULL;
365 while (*SystemRootName != L'\\')
366 {
367 ASSERT(SystemRootName > SymbolicLinkName.Buffer);
368 SystemRootName--;
369 }
370 *SystemRootName++ = UNICODE_NULL;
371 trace("System Drive: '%ls'\n", SystemDriveName);
372 trace("System Root: '%ls'\n", SystemRootName);
373
374 /* Allocate path buffer */
375 Buffer = ExAllocatePoolWithTag(PagedPool, MAXUSHORT, 'sFmK');
376 if (skip(Buffer != NULL, "No buffer\n"))
377 return;
378
379 /* Finally run some tests! */
380 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
381 {
382 /* Open parent directory first */
383 ParentHandle = NULL;
384 if (Tests[i].ParentPathTemplate)
385 {
386 Substitute(Buffer,
387 MAXUSHORT,
388 Tests[i].ParentPathTemplate,
389 SystemDriveName,
390 SystemRootName);
391 RtlInitUnicodeString(&ParentPath, Buffer);
392 InitializeObjectAttributes(&ObjectAttributes,
393 &ParentPath,
394 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
395 NULL,
396 NULL);
397 Status = ZwOpenFile(&ParentHandle,
398 GENERIC_READ,
399 &ObjectAttributes,
400 &IoStatus,
401 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
402 0);
403 ok(Status == STATUS_SUCCESS,
404 "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
405 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
406 continue;
407 }
408
409 /* Now open the relative file: */
410 Substitute(Buffer,
411 MAXUSHORT,
412 Tests[i].RelativePathTemplate,
413 SystemDriveName,
414 SystemRootName);
415 RtlInitUnicodeString(&RelativePath, Buffer);
416 InitializeObjectAttributes(&ObjectAttributes,
417 &RelativePath,
418 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
419 ParentHandle,
420 NULL);
421 TrailingBackslash = FALSE;
422 if (wcslen(Buffer) && Buffer[wcslen(Buffer) - 1] == L'\\')
423 TrailingBackslash = TRUE;
424
425 /* (1) No flags */
426 Status = ZwOpenFile(&FileHandle,
427 GENERIC_READ,
428 &ObjectAttributes,
429 &IoStatus,
430 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
431 0);
432 ok(Status == Tests[i].Status,
433 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
434 if (NT_SUCCESS(Status))
435 ObCloseHandle(FileHandle, KernelMode);
436
437 /* (2) Directory File */
438 Status = ZwOpenFile(&FileHandle,
439 GENERIC_READ,
440 &ObjectAttributes,
441 &IoStatus,
442 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
443 FILE_DIRECTORY_FILE);
444 if (Tests[i].IsDirectory || (!TrailingBackslash && !NT_SUCCESS(Tests[i].Status)))
445 ok(Status == Tests[i].Status,
446 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
447 else
448 ok(Status == STATUS_NOT_A_DIRECTORY,
449 "[%lu] Status = %lx, expected STATUS_NOT_A_DIRECTORY\n", i, Status);
450 if (NT_SUCCESS(Status))
451 ObCloseHandle(FileHandle, KernelMode);
452
453 /* (3) Non-Directory File */
454 Status = ZwOpenFile(&FileHandle,
455 GENERIC_READ,
456 &ObjectAttributes,
457 &IoStatus,
458 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
459 FILE_NON_DIRECTORY_FILE);
460 if (Tests[i].IsDirectory && NT_SUCCESS(Tests[i].Status))
461 ok(Status == STATUS_FILE_IS_A_DIRECTORY,
462 "[%lu] Status = %lx, expected STATUS_FILE_IS_A_DIRECTORY\n", i, Status);
463 else
464 ok(Status == Tests[i].Status,
465 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
466 if (NT_SUCCESS(Status))
467 ObCloseHandle(FileHandle, KernelMode);
468
469 /* And close */
470 ObCloseHandle(ParentHandle, KernelMode);
471 }
472
473 ExFreePoolWithTag(Buffer, 'sFmK');
474 }
475
476 static
477 VOID
478 TestSharedCacheMap(VOID)
479 {
480 NTSTATUS Status;
481 struct
482 {
483 PCWSTR ParentPath;
484 PCWSTR RelativePath;
485 } Tests[] =
486 {
487 { 0, L"\\SystemRoot\\system32\\drivers\\etc\\hosts" },
488 { L"\\SystemRoot", L"system32\\drivers\\etc\\hosts" },
489 { L"\\SystemRoot\\system32", L"drivers\\etc\\hosts" },
490 { L"\\SystemRoot\\system32\\drivers", L"etc\\hosts" },
491 { L"\\SystemRoot\\system32\\drivers\\etc", L"hosts" },
492 };
493 OBJECT_ATTRIBUTES ObjectAttributes;
494 IO_STATUS_BLOCK IoStatus;
495 UNICODE_STRING ParentPath;
496 UNICODE_STRING RelativePath;
497 HANDLE ParentHandle[RTL_NUMBER_OF(Tests)] = { NULL };
498 HANDLE FileHandle[RTL_NUMBER_OF(Tests)] = { NULL };
499 PFILE_OBJECT FileObject[RTL_NUMBER_OF(Tests)] = { NULL };
500 PFILE_OBJECT SystemRootObject = NULL;
501 UCHAR Buffer[32];
502 HANDLE EventHandle;
503 LARGE_INTEGER FileOffset;
504 ULONG i;
505
506 /* We need an event for ZwReadFile */
507 InitializeObjectAttributes(&ObjectAttributes,
508 NULL,
509 OBJ_KERNEL_HANDLE,
510 NULL,
511 NULL);
512 Status = ZwCreateEvent(&EventHandle,
513 SYNCHRONIZE,
514 &ObjectAttributes,
515 NotificationEvent,
516 FALSE);
517 if (skip(NT_SUCCESS(Status), "No event\n"))
518 goto Cleanup;
519
520 /* Open all test files and get their FILE_OBJECT pointers */
521 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
522 {
523 if (Tests[i].ParentPath)
524 {
525 RtlInitUnicodeString(&ParentPath, Tests[i].ParentPath);
526 InitializeObjectAttributes(&ObjectAttributes,
527 &ParentPath,
528 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
529 NULL,
530 NULL);
531 Status = ZwOpenFile(&ParentHandle[i],
532 GENERIC_READ,
533 &ObjectAttributes,
534 &IoStatus,
535 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
536 0);
537 ok_eq_hex(Status, STATUS_SUCCESS);
538 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
539 goto Cleanup;
540 }
541
542 RtlInitUnicodeString(&RelativePath, Tests[i].RelativePath);
543 InitializeObjectAttributes(&ObjectAttributes,
544 &RelativePath,
545 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
546 ParentHandle[i],
547 NULL);
548 Status = ZwOpenFile(&FileHandle[i],
549 FILE_ALL_ACCESS,
550 &ObjectAttributes,
551 &IoStatus,
552 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
553 0);
554 ok_eq_hex(Status, STATUS_SUCCESS);
555 if (skip(NT_SUCCESS(Status), "No file handle %lu\n", i))
556 goto Cleanup;
557
558 Status = ObReferenceObjectByHandle(FileHandle[i],
559 FILE_ALL_ACCESS,
560 *IoFileObjectType,
561 KernelMode,
562 (PVOID*)&FileObject[i],
563 NULL);
564 ok_eq_hex(Status, STATUS_SUCCESS);
565 if (skip(NT_SUCCESS(Status), "No file object %lu\n", i))
566 goto Cleanup;
567 }
568
569 /* Also get a file object for the SystemRoot directory */
570 Status = ObReferenceObjectByHandle(ParentHandle[1],
571 GENERIC_READ,
572 *IoFileObjectType,
573 KernelMode,
574 (PVOID*)&SystemRootObject,
575 NULL);
576 ok_eq_hex(Status, STATUS_SUCCESS);
577 if (skip(NT_SUCCESS(Status), "No SystemRoot object\n"))
578 goto Cleanup;
579
580 /* Before read, caching is not initialized */
581 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
582 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
583 {
584 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
585 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
586 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
587 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
588 }
589 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
590 ok_eq_pointer(FileObject[0]->SectionObjectPointer->SharedCacheMap, NULL);
591
592 /* Perform a read on one handle to initialize caching */
593 FileOffset.QuadPart = 0;
594 Status = ZwReadFile(FileHandle[0],
595 EventHandle,
596 NULL,
597 NULL,
598 &IoStatus,
599 Buffer,
600 sizeof(Buffer),
601 &FileOffset,
602 NULL);
603 if (Status == STATUS_PENDING)
604 {
605 Status = ZwWaitForSingleObject(EventHandle, FALSE, NULL);
606 ok_eq_hex(Status, STATUS_SUCCESS);
607 Status = IoStatus.Status;
608 }
609 ok_eq_hex(Status, STATUS_SUCCESS);
610
611 /* Now we see a SharedCacheMap for the file */
612 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
613 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
614 {
615 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
616 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
617 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
618 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
619 }
620 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
621 ok(FileObject[0]->SectionObjectPointer->SharedCacheMap != NULL, "SharedCacheMap is NULL\n");
622
623 Cleanup:
624 if (SystemRootObject)
625 ObDereferenceObject(SystemRootObject);
626 if (EventHandle)
627 ObCloseHandle(EventHandle, KernelMode);
628 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
629 {
630 if (FileObject[i])
631 ObDereferenceObject(FileObject[i]);
632 if (FileHandle[i])
633 ObCloseHandle(FileHandle[i], KernelMode);
634 if (ParentHandle[i])
635 ObCloseHandle(ParentHandle[i], KernelMode);
636 }
637 }
638
639 START_TEST(IoFilesystem)
640 {
641 TestAllInformation();
642 TestRelativeNames();
643 TestSharedCacheMap();
644 }