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