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