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