[KMTESTS:CC]
[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 BOOLEAN IsDrive;
281 } Tests[] =
282 {
283 { NULL, L"C:\\", TRUE, STATUS_SUCCESS, TRUE },
284 { NULL, L"C:\\\\", TRUE, STATUS_SUCCESS, TRUE },
285 { NULL, L"C:\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID, TRUE },
286 { NULL, L"C:\\ReactOS", TRUE, STATUS_SUCCESS },
287 { NULL, L"C:\\ReactOS\\", TRUE, STATUS_SUCCESS },
288 { NULL, L"C:\\ReactOS\\\\", TRUE, STATUS_SUCCESS },
289 { NULL, L"C:\\ReactOS\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
290 { NULL, L"C:\\\\ReactOS", TRUE, STATUS_SUCCESS },
291 { NULL, L"C:\\\\ReactOS\\", TRUE, STATUS_SUCCESS },
292 { NULL, L"C:\\ReactOS\\explorer.exe", FALSE, STATUS_SUCCESS },
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 { NULL, L"C:\\ReactOS\\explorer.exe\\\\", FALSE, STATUS_OBJECT_NAME_INVALID },
296 /* This will never return STATUS_NOT_A_DIRECTORY. IsDirectory=TRUE is a little hacky but achieves that without special handling */
297 { NULL, L"C:\\ReactOS\\explorer.exe\\\\\\", TRUE, STATUS_OBJECT_NAME_INVALID },
298 { L"C:\\", L"", TRUE, STATUS_SUCCESS },
299 { L"C:\\", L"\\", TRUE, STATUS_OBJECT_NAME_INVALID },
300 { L"C:\\", L"ReactOS", TRUE, STATUS_SUCCESS },
301 { L"C:\\", L"\\ReactOS", TRUE, STATUS_OBJECT_NAME_INVALID },
302 { L"C:\\", L"ReactOS\\", TRUE, STATUS_SUCCESS },
303 { L"C:\\", L"\\ReactOS\\", TRUE, STATUS_OBJECT_NAME_INVALID },
304 { L"C:\\ReactOS", L"", TRUE, STATUS_SUCCESS },
305 { L"C:\\ReactOS", L"explorer.exe", FALSE, STATUS_SUCCESS },
306 { L"C:\\ReactOS\\explorer.exe", L"", FALSE, STATUS_SUCCESS },
307 { L"C:\\ReactOS\\explorer.exe", L"file", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
308 /* Let's try some nonexistent things */
309 { NULL, L"C:\\ReactOS\\IDoNotExist", FALSE, STATUS_OBJECT_NAME_NOT_FOUND },
310 { NULL, L"C:\\ReactOS\\IDoNotExist\\file", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
311 { NULL, L"C:\\ReactOS\\IDoNotExist\\file?", FALSE, STATUS_OBJECT_PATH_NOT_FOUND },
312 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
313 { NULL, L"C:\\ReactOS\\IDoNotExist\\file\\\\\\",TRUE,STATUS_OBJECT_PATH_NOT_FOUND },
314 { NULL, L"C:\\ReactOS\\AmIInvalid?", FALSE, STATUS_OBJECT_NAME_INVALID },
315 { NULL, L"C:\\ReactOS\\.", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
316 { NULL, L"C:\\ReactOS\\..", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
317 { NULL, L"C:\\ReactOS\\...", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
318 { NULL, L"C:\\ReactOS\\.\\system32", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
319 { NULL, L"C:\\ReactOS\\..\\ReactOS", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
320 { L"C:\\", L".", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
321 { L"C:\\", L"..", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
322 { L"C:\\", L"...", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
323 { L"C:\\", L".\\ReactOS", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
324 { L"C:\\", L"..\\ReactOS", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
325 { L"C:\\ReactOS", L".", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
326 { L"C:\\ReactOS", L"..", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
327 { L"C:\\ReactOS", L"...", TRUE, STATUS_OBJECT_NAME_NOT_FOUND },
328 { L"C:\\ReactOS", L".\\system32", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
329 { L"C:\\ReactOS", L"..\\ReactOS", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
330 /* Volume open */
331 { NULL, L"C:", FALSE, STATUS_SUCCESS, TRUE },
332 { L"C:", L"", FALSE, STATUS_SUCCESS, TRUE },
333 { L"C:", L"\\", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
334 { L"C:", L"file", TRUE, STATUS_OBJECT_PATH_NOT_FOUND },
335 };
336 ULONG i;
337 OBJECT_ATTRIBUTES ObjectAttributes;
338 IO_STATUS_BLOCK IoStatus;
339 UNICODE_STRING ParentPath;
340 UNICODE_STRING RelativePath;
341 HANDLE ParentHandle;
342 HANDLE FileHandle;
343 UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
344 HANDLE SymbolicLinkHandle = NULL;
345 WCHAR LinkNameBuffer[128];
346 UNICODE_STRING SymbolicLinkName;
347 PWSTR SystemDriveName;
348 PWSTR SystemRootName;
349 PWCHAR Buffer = NULL;
350 BOOLEAN TrailingBackslash;
351 LARGE_INTEGER AllocationSize;
352 FILE_DISPOSITION_INFORMATION DispositionInfo;
353
354 /* Query \SystemRoot */
355 InitializeObjectAttributes(&ObjectAttributes,
356 &SystemRoot,
357 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
358 NULL,
359 NULL);
360 Status = ZwOpenSymbolicLinkObject(&SymbolicLinkHandle,
361 GENERIC_READ,
362 &ObjectAttributes);
363 if (skip(NT_SUCCESS(Status), "Failed to open SystemRoot, %lx\n", Status))
364 return;
365
366 RtlInitEmptyUnicodeString(&SymbolicLinkName,
367 LinkNameBuffer,
368 sizeof(LinkNameBuffer));
369 Status = ZwQuerySymbolicLinkObject(SymbolicLinkHandle,
370 &SymbolicLinkName,
371 NULL);
372 ObCloseHandle(SymbolicLinkHandle, KernelMode);
373 if (skip(NT_SUCCESS(Status), "Failed to query SystemRoot, %lx\n", Status))
374 return;
375
376 /* Split SymbolicLinkName into drive and path */
377 SystemDriveName = SymbolicLinkName.Buffer;
378 SystemRootName = SymbolicLinkName.Buffer + SymbolicLinkName.Length / sizeof(WCHAR);
379 *SystemRootName-- = UNICODE_NULL;
380 while (*SystemRootName != L'\\')
381 {
382 ASSERT(SystemRootName > SymbolicLinkName.Buffer);
383 SystemRootName--;
384 }
385 *SystemRootName++ = UNICODE_NULL;
386 trace("System Drive: '%ls'\n", SystemDriveName);
387 trace("System Root: '%ls'\n", SystemRootName);
388
389 /* Allocate path buffer */
390 Buffer = ExAllocatePoolWithTag(PagedPool, MAXUSHORT, 'sFmK');
391 if (skip(Buffer != NULL, "No buffer\n"))
392 return;
393
394 /* Finally run some tests! */
395 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
396 {
397 /* Open parent directory first */
398 ParentHandle = NULL;
399 if (Tests[i].ParentPathTemplate)
400 {
401 Substitute(Buffer,
402 MAXUSHORT,
403 Tests[i].ParentPathTemplate,
404 SystemDriveName,
405 SystemRootName);
406 RtlInitUnicodeString(&ParentPath, Buffer);
407 InitializeObjectAttributes(&ObjectAttributes,
408 &ParentPath,
409 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
410 NULL,
411 NULL);
412 Status = ZwOpenFile(&ParentHandle,
413 GENERIC_READ,
414 &ObjectAttributes,
415 &IoStatus,
416 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
417 0);
418 ok(Status == STATUS_SUCCESS,
419 "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
420 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
421 continue;
422 }
423
424 /* Now open the relative file: */
425 Substitute(Buffer,
426 MAXUSHORT,
427 Tests[i].RelativePathTemplate,
428 SystemDriveName,
429 SystemRootName);
430 RtlInitUnicodeString(&RelativePath, Buffer);
431 InitializeObjectAttributes(&ObjectAttributes,
432 &RelativePath,
433 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
434 ParentHandle,
435 NULL);
436 TrailingBackslash = FALSE;
437 if (wcslen(Buffer) && Buffer[wcslen(Buffer) - 1] == L'\\')
438 TrailingBackslash = TRUE;
439
440 /* (1) No flags */
441 Status = ZwOpenFile(&FileHandle,
442 GENERIC_READ,
443 &ObjectAttributes,
444 &IoStatus,
445 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
446 0);
447 ok(Status == Tests[i].Status,
448 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
449 if (NT_SUCCESS(Status))
450 ObCloseHandle(FileHandle, KernelMode);
451
452 /* (2) Directory File */
453 Status = ZwOpenFile(&FileHandle,
454 GENERIC_READ,
455 &ObjectAttributes,
456 &IoStatus,
457 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
458 FILE_DIRECTORY_FILE);
459 if (Tests[i].IsDirectory || (!TrailingBackslash && !NT_SUCCESS(Tests[i].Status)))
460 ok(Status == Tests[i].Status,
461 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
462 else
463 ok(Status == STATUS_NOT_A_DIRECTORY,
464 "[%lu] Status = %lx, expected STATUS_NOT_A_DIRECTORY\n", i, Status);
465 if (NT_SUCCESS(Status))
466 ObCloseHandle(FileHandle, KernelMode);
467
468 /* (3) Non-Directory File */
469 Status = ZwOpenFile(&FileHandle,
470 GENERIC_READ,
471 &ObjectAttributes,
472 &IoStatus,
473 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
474 FILE_NON_DIRECTORY_FILE);
475 if (Tests[i].IsDirectory && NT_SUCCESS(Tests[i].Status))
476 ok(Status == STATUS_FILE_IS_A_DIRECTORY,
477 "[%lu] Status = %lx, expected STATUS_FILE_IS_A_DIRECTORY\n", i, Status);
478 else
479 ok(Status == Tests[i].Status,
480 "[%lu] Status = %lx, expected %lx\n", i, Status, Tests[i].Status);
481 if (NT_SUCCESS(Status))
482 ObCloseHandle(FileHandle, KernelMode);
483
484 /* (4) Directory + Non-Directory */
485 Status = ZwOpenFile(&FileHandle,
486 GENERIC_READ,
487 &ObjectAttributes,
488 &IoStatus,
489 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
490 FILE_DIRECTORY_FILE | FILE_NON_DIRECTORY_FILE);
491 if (Tests[i].Status == STATUS_OBJECT_NAME_INVALID && Tests[i].IsDrive)
492 ok(Status == STATUS_OBJECT_NAME_INVALID,
493 "[%lu] Status = %lx, expected STATUS_OBJECT_NAME_INVALID\n", i, Status);
494 else
495 ok(Status == STATUS_INVALID_PARAMETER,
496 "[%lu] Status = %lx, expected STATUS_INVALID_PARAMETER\n", i, Status);
497 if (NT_SUCCESS(Status))
498 ObCloseHandle(FileHandle, KernelMode);
499
500 /* (5) Try to create it */
501 AllocationSize.QuadPart = 0;
502 Status = ZwCreateFile(&FileHandle,
503 GENERIC_READ | DELETE,
504 &ObjectAttributes,
505 &IoStatus,
506 &AllocationSize,
507 FILE_ATTRIBUTE_NORMAL,
508 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
509 FILE_CREATE,
510 0,
511 NULL,
512 0);
513 if (Tests[i].Status == STATUS_OBJECT_NAME_NOT_FOUND)
514 ok(Status == STATUS_SUCCESS,
515 "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
516 else if (Tests[i].Status == STATUS_OBJECT_NAME_INVALID && Tests[i].IsDrive)
517 ok(Status == STATUS_OBJECT_NAME_INVALID,
518 "[%lu] Status = %lx, expected STATUS_OBJECT_NAME_INVALID\n", i, Status);else if (Tests[i].IsDrive)
519 ok(Status == STATUS_ACCESS_DENIED,
520 "[%lu] Status = %lx, expected STATUS_ACCESS_DENIED\n", i, Status);
521 else if (Tests[i].Status == STATUS_SUCCESS)
522 ok(Status == STATUS_OBJECT_NAME_COLLISION,
523 "[%lu] Status = %lx, expected STATUS_OBJECT_NAME_COLLISION\n", i, Status);
524 else
525 ok(Status == Tests[i].Status,
526 "[%lu] Status = %lx, expected %lx; %ls -- %ls\n", i, Status, Tests[i].Status, Tests[i].ParentPathTemplate, Tests[i].RelativePathTemplate);
527 if (NT_SUCCESS(Status))
528 {
529 if (IoStatus.Information == FILE_CREATED)
530 {
531 DispositionInfo.DeleteFile = TRUE;
532 Status = ZwSetInformationFile(FileHandle,
533 &IoStatus,
534 &DispositionInfo,
535 sizeof(DispositionInfo),
536 FileDispositionInformation);
537 ok(Status == STATUS_SUCCESS,
538 "[%lu] Status = %lx, expected STATUS_SUCCESS\n", i, Status);
539 }
540 ObCloseHandle(FileHandle, KernelMode);
541 }
542
543 /* And close */
544 ObCloseHandle(ParentHandle, KernelMode);
545 }
546
547 ExFreePoolWithTag(Buffer, 'sFmK');
548 }
549
550 static
551 VOID
552 TestSharedCacheMap(VOID)
553 {
554 NTSTATUS Status;
555 struct
556 {
557 PCWSTR ParentPath;
558 PCWSTR RelativePath;
559 } Tests[] =
560 {
561 { 0, L"\\SystemRoot\\system32\\drivers\\etc\\hosts" },
562 { L"\\SystemRoot", L"system32\\drivers\\etc\\hosts" },
563 { L"\\SystemRoot\\system32", L"drivers\\etc\\hosts" },
564 { L"\\SystemRoot\\system32\\drivers", L"etc\\hosts" },
565 { L"\\SystemRoot\\system32\\drivers\\etc", L"hosts" },
566 };
567 OBJECT_ATTRIBUTES ObjectAttributes;
568 IO_STATUS_BLOCK IoStatus;
569 UNICODE_STRING ParentPath;
570 UNICODE_STRING RelativePath;
571 HANDLE ParentHandle[RTL_NUMBER_OF(Tests)] = { NULL };
572 HANDLE FileHandle[RTL_NUMBER_OF(Tests)] = { NULL };
573 PFILE_OBJECT FileObject[RTL_NUMBER_OF(Tests)] = { NULL };
574 PFILE_OBJECT SystemRootObject = NULL;
575 UCHAR Buffer[32];
576 HANDLE EventHandle;
577 LARGE_INTEGER FileOffset;
578 ULONG i;
579
580 /* We need an event for ZwReadFile */
581 InitializeObjectAttributes(&ObjectAttributes,
582 NULL,
583 OBJ_KERNEL_HANDLE,
584 NULL,
585 NULL);
586 Status = ZwCreateEvent(&EventHandle,
587 SYNCHRONIZE,
588 &ObjectAttributes,
589 NotificationEvent,
590 FALSE);
591 if (skip(NT_SUCCESS(Status), "No event\n"))
592 goto Cleanup;
593
594 /* Open all test files and get their FILE_OBJECT pointers */
595 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
596 {
597 if (Tests[i].ParentPath)
598 {
599 RtlInitUnicodeString(&ParentPath, Tests[i].ParentPath);
600 InitializeObjectAttributes(&ObjectAttributes,
601 &ParentPath,
602 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
603 NULL,
604 NULL);
605 Status = ZwOpenFile(&ParentHandle[i],
606 GENERIC_READ,
607 &ObjectAttributes,
608 &IoStatus,
609 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
610 0);
611 ok_eq_hex(Status, STATUS_SUCCESS);
612 if (skip(NT_SUCCESS(Status), "No parent handle %lu\n", i))
613 goto Cleanup;
614 }
615
616 RtlInitUnicodeString(&RelativePath, Tests[i].RelativePath);
617 InitializeObjectAttributes(&ObjectAttributes,
618 &RelativePath,
619 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
620 ParentHandle[i],
621 NULL);
622 Status = ZwOpenFile(&FileHandle[i],
623 FILE_ALL_ACCESS,
624 &ObjectAttributes,
625 &IoStatus,
626 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
627 0);
628 ok_eq_hex(Status, STATUS_SUCCESS);
629 if (skip(NT_SUCCESS(Status), "No file handle %lu\n", i))
630 goto Cleanup;
631
632 Status = ObReferenceObjectByHandle(FileHandle[i],
633 FILE_ALL_ACCESS,
634 *IoFileObjectType,
635 KernelMode,
636 (PVOID*)&FileObject[i],
637 NULL);
638 ok_eq_hex(Status, STATUS_SUCCESS);
639 if (skip(NT_SUCCESS(Status), "No file object %lu\n", i))
640 goto Cleanup;
641 }
642
643 /* Also get a file object for the SystemRoot directory */
644 Status = ObReferenceObjectByHandle(ParentHandle[1],
645 GENERIC_READ,
646 *IoFileObjectType,
647 KernelMode,
648 (PVOID*)&SystemRootObject,
649 NULL);
650 ok_eq_hex(Status, STATUS_SUCCESS);
651 if (skip(NT_SUCCESS(Status), "No SystemRoot object\n"))
652 goto Cleanup;
653
654 /* Before read, caching is not initialized */
655 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
656 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
657 {
658 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
659 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
660 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
661 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
662 }
663 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
664 ok_eq_pointer(FileObject[0]->SectionObjectPointer->SharedCacheMap, NULL);
665
666 /* Perform a read on one handle to initialize caching */
667 FileOffset.QuadPart = 0;
668 Status = ZwReadFile(FileHandle[0],
669 EventHandle,
670 NULL,
671 NULL,
672 &IoStatus,
673 Buffer,
674 sizeof(Buffer),
675 &FileOffset,
676 NULL);
677 if (Status == STATUS_PENDING)
678 {
679 Status = ZwWaitForSingleObject(EventHandle, FALSE, NULL);
680 ok_eq_hex(Status, STATUS_SUCCESS);
681 Status = IoStatus.Status;
682 }
683 ok_eq_hex(Status, STATUS_SUCCESS);
684
685 /* Now we see a SharedCacheMap for the file */
686 ok_eq_pointer(SystemRootObject->SectionObjectPointer, NULL);
687 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
688 {
689 ok(FileObject[i]->SectionObjectPointer != NULL, "FileObject[%lu]->SectionObjectPointer = NULL\n", i);
690 ok(FileObject[i]->SectionObjectPointer == FileObject[0]->SectionObjectPointer,
691 "FileObject[%lu]->SectionObjectPointer = %p, expected %p\n",
692 i, FileObject[i]->SectionObjectPointer, FileObject[0]->SectionObjectPointer);
693 }
694 if (!skip(FileObject[0]->SectionObjectPointer != NULL, "No section object pointers\n"))
695 ok(FileObject[0]->SectionObjectPointer->SharedCacheMap != NULL, "SharedCacheMap is NULL\n");
696
697 Cleanup:
698 if (SystemRootObject)
699 ObDereferenceObject(SystemRootObject);
700 if (EventHandle)
701 ObCloseHandle(EventHandle, KernelMode);
702 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
703 {
704 if (FileObject[i])
705 ObDereferenceObject(FileObject[i]);
706 if (FileHandle[i])
707 ObCloseHandle(FileHandle[i], KernelMode);
708 if (ParentHandle[i])
709 ObCloseHandle(ParentHandle[i], KernelMode);
710 }
711 }
712
713 START_TEST(IoFilesystem)
714 {
715 TestAllInformation();
716 TestRelativeNames();
717 TestSharedCacheMap();
718 }