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
QueryFileInfo(_In_ HANDLE FileHandle,_Out_ PVOID * Info,_Inout_ PSIZE_T Length,_In_ FILE_INFORMATION_CLASS FileInformationClass)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
TestAllInformation(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
Substitute(_Out_writes_bytes_ (BufferSize)PWCHAR Buffer,_In_ ULONG BufferSize,_In_ PCWSTR Template,_In_ PCWSTR SystemDriveName,_In_ PCWSTR SystemRootName)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
TestRelativeNames(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
TestSharedCacheMap(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
START_TEST(IoFilesystem)746 START_TEST(IoFilesystem)
747 {
748 TestAllInformation();
749 TestRelativeNames();
750 TestSharedCacheMap();
751 }
752