1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for CcMapData function
5  * PROGRAMMER:      Pierre Schweitzer <pierre@reactos.org>
6  */
7 
8 #include <kmt_test.h>
9 
10 #define NDEBUG
11 #include <debug.h>
12 
13 #define IOCTL_START_TEST  1
14 #define IOCTL_FINISH_TEST 2
15 
16 typedef struct _TEST_FCB
17 {
18     FSRTL_ADVANCED_FCB_HEADER Header;
19     SECTION_OBJECT_POINTERS SectionObjectPointers;
20     FAST_MUTEX HeaderMutex;
21 } TEST_FCB, *PTEST_FCB;
22 
23 typedef struct _TEST_CONTEXT
24 {
25     PVOID Bcb;
26     PVOID Buffer;
27     ULONG Length;
28 } TEST_CONTEXT, *PTEST_CONTEXT;
29 
30 static ULONG TestTestId = -1;
31 static PFILE_OBJECT TestFileObject;
32 static PDEVICE_OBJECT TestDeviceObject;
33 static KMT_IRP_HANDLER TestIrpHandler;
34 static KMT_MESSAGE_HANDLER TestMessageHandler;
35 
36 NTSTATUS
37 TestEntry(
38     _In_ PDRIVER_OBJECT DriverObject,
39     _In_ PCUNICODE_STRING RegistryPath,
40     _Out_ PCWSTR *DeviceName,
41     _Inout_ INT *Flags)
42 {
43     NTSTATUS Status = STATUS_SUCCESS;
44 
45     PAGED_CODE();
46 
47     UNREFERENCED_PARAMETER(RegistryPath);
48 
49     *DeviceName = L"CcMapData";
50     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
51              TESTENTRY_BUFFERED_IO_DEVICE |
52              TESTENTRY_NO_READONLY_DEVICE;
53 
54     KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
55     KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
56 
57 
58     return Status;
59 }
60 
61 VOID
62 TestUnload(
63     _In_ PDRIVER_OBJECT DriverObject)
64 {
65     PAGED_CODE();
66 }
67 
68 BOOLEAN
69 NTAPI
70 AcquireForLazyWrite(
71     _In_ PVOID Context,
72     _In_ BOOLEAN Wait)
73 {
74     return TRUE;
75 }
76 
77 VOID
78 NTAPI
79 ReleaseFromLazyWrite(
80     _In_ PVOID Context)
81 {
82     return;
83 }
84 
85 BOOLEAN
86 NTAPI
87 AcquireForReadAhead(
88     _In_ PVOID Context,
89     _In_ BOOLEAN Wait)
90 {
91     return TRUE;
92 }
93 
94 VOID
95 NTAPI
96 ReleaseFromReadAhead(
97     _In_ PVOID Context)
98 {
99     return;
100 }
101 
102 static CACHE_MANAGER_CALLBACKS Callbacks = {
103     AcquireForLazyWrite,
104     ReleaseFromLazyWrite,
105     AcquireForReadAhead,
106     ReleaseFromReadAhead,
107 };
108 
109 static CC_FILE_SIZES FileSizes = {
110     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .AllocationSize
111     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .FileSize
112     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000)  // .ValidDataLength
113 };
114 
115 static
116 PVOID
117 MapAndLockUserBuffer(
118     _In_ _Out_ PIRP Irp,
119     _In_ ULONG BufferLength)
120 {
121     PMDL Mdl;
122 
123     if (Irp->MdlAddress == NULL)
124     {
125         Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
126         if (Mdl == NULL)
127         {
128             return NULL;
129         }
130 
131         _SEH2_TRY
132         {
133             MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess);
134         }
135         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
136         {
137             IoFreeMdl(Mdl);
138             Irp->MdlAddress = NULL;
139             _SEH2_YIELD(return NULL);
140         }
141         _SEH2_END;
142     }
143 
144     return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
145 }
146 
147 static
148 VOID
149 NTAPI
150 MapInAnotherThread(IN PVOID Context)
151 {
152     BOOLEAN Ret;
153     PULONG Buffer;
154     PVOID Bcb;
155     LARGE_INTEGER Offset;
156     PTEST_CONTEXT TestContext;
157 
158     ok(TestFileObject != NULL, "Called in invalid context!\n");
159     ok_eq_ulong(TestTestId, 3);
160 
161     TestContext = Context;
162     ok(TestContext != NULL, "Called in invalid context!\n");
163     ok(TestContext->Bcb != NULL, "Called in invalid context!\n");
164     ok(TestContext->Buffer != NULL, "Called in invalid context!\n");
165     ok(TestContext->Length != 0, "Called in invalid context!\n");
166 
167     Ret = FALSE;
168     Offset.QuadPart = 0x1000;
169     KmtStartSeh();
170     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
171     KmtEndSeh(STATUS_SUCCESS);
172 
173     if (!skip(Ret == TRUE, "CcMapData failed\n"))
174     {
175         ok_eq_pointer(Bcb, TestContext->Bcb);
176         ok_eq_pointer(Buffer, TestContext->Buffer);
177 
178         CcUnpinData(Bcb);
179     }
180 
181     KmtStartSeh();
182     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer);
183     KmtEndSeh(STATUS_SUCCESS);
184 
185     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
186     {
187         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
188         ok_eq_pointer(Buffer, TestContext->Buffer);
189 
190         CcUnpinData(Bcb);
191     }
192 
193     KmtStartSeh();
194     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
195     KmtEndSeh(STATUS_SUCCESS);
196     ok(Ret == FALSE, "CcPinRead succeed\n");
197 
198     if (Ret)
199     {
200         CcUnpinData(Bcb);
201     }
202 
203     KmtStartSeh();
204     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer);
205     KmtEndSeh(STATUS_SUCCESS);
206 
207     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
208     {
209         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
210         ok_eq_pointer(Buffer, TestContext->Buffer);
211 
212         CcUnpinData(Bcb);
213     }
214 
215     Offset.QuadPart = 0x1500;
216     TestContext->Length -= 0x500;
217 
218     KmtStartSeh();
219     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
220     KmtEndSeh(STATUS_SUCCESS);
221 
222     if (!skip(Ret == TRUE, "CcMapData failed\n"))
223     {
224         ok_eq_pointer(Bcb, TestContext->Bcb);
225         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
226 
227         CcUnpinData(Bcb);
228     }
229 
230     return;
231 }
232 
233 static
234 VOID
235 PerformTest(
236     ULONG TestId,
237     PDEVICE_OBJECT DeviceObject)
238 {
239     PVOID Bcb;
240     BOOLEAN Ret;
241     PULONG Buffer;
242     PTEST_FCB Fcb;
243     LARGE_INTEGER Offset;
244 
245     ok_eq_pointer(TestFileObject, NULL);
246     ok_eq_pointer(TestDeviceObject, NULL);
247     ok_eq_ulong(TestTestId, -1);
248 
249     TestDeviceObject = DeviceObject;
250     TestTestId = TestId;
251     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
252     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
253     {
254         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
255         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
256         {
257             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
258             ExInitializeFastMutex(&Fcb->HeaderMutex);
259             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
260 
261             TestFileObject->FsContext = Fcb;
262             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
263 
264             KmtStartSeh();
265             CcInitializeCacheMap(TestFileObject, &FileSizes, FALSE, &Callbacks, NULL);
266             KmtEndSeh(STATUS_SUCCESS);
267 
268             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
269             {
270                 if (TestId < 3)
271                 {
272                     Ret = FALSE;
273                     Offset.QuadPart = TestId * 0x1000;
274                     KmtStartSeh();
275                     Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
276                     KmtEndSeh(STATUS_SUCCESS);
277 
278                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
279                     {
280                         ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE);
281 
282                         CcUnpinData(Bcb);
283                     }
284                 }
285                 else if (TestId == 3)
286                 {
287                     PTEST_CONTEXT TestContext;
288 
289                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
290                     if (!skip(TestContext != NULL, "ExAllocatePool failed\n"))
291                     {
292                         Ret = FALSE;
293                         Offset.QuadPart = 0x1000;
294                         KmtStartSeh();
295                         Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &TestContext->Bcb, &TestContext->Buffer);
296                         KmtEndSeh(STATUS_SUCCESS);
297 
298                         if (!skip(Ret == TRUE, "CcMapData failed\n"))
299                         {
300                             PKTHREAD ThreadHandle;
301 
302 #ifdef _X86_
303                             /* FIXME: Should be fixed, will fail under certains conditions */
304                             ok(TestContext->Buffer > (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF,
305                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
306 #else
307 #ifdef _M_AMD64
308                             ok(TestContext->Buffer > (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000,
309                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
310 #else
311                             skip(FALSE, "System space mapping not defined\n");
312 #endif
313 #endif
314 
315                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
316                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
317                             KmtFinishThread(ThreadHandle, NULL);
318 
319                             TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart;
320                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
321                             KmtFinishThread(ThreadHandle, NULL);
322 
323                             CcUnpinData(TestContext->Bcb);
324                         }
325 
326                         ExFreePool(TestContext);
327                     }
328                 }
329                 else if (TestId == 4)
330                 {
331                     /* Map after EOF */
332                     Ret = FALSE;
333                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000;
334 
335                     KmtStartSeh();
336                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer);
337                     KmtEndSeh(STATUS_SUCCESS);
338                     ok(Ret == FALSE, "CcMapData succeed\n");
339 
340                     if (Ret)
341                     {
342                         CcUnpinData(Bcb);
343                     }
344 
345                     /* Map a VACB after EOF */
346                     Ret = FALSE;
347                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY;
348 
349                     KmtStartSeh();
350                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer);
351                     KmtEndSeh(STATUS_ACCESS_VIOLATION);
352                     ok(Ret == FALSE, "CcMapData succeed\n");
353 
354                     if (Ret)
355                     {
356                         CcUnpinData(Bcb);
357                     }
358 
359                     /* Map more than a VACB */
360                     Ret = FALSE;
361                     Offset.QuadPart = 0x0;
362 
363                     KmtStartSeh();
364                     Ret = CcMapData(TestFileObject, &Offset, 0x1000 + VACB_MAPPING_GRANULARITY, 0, &Bcb, (PVOID *)&Buffer);
365                     KmtEndSeh(STATUS_SUCCESS);
366                     ok(Ret == FALSE, "CcMapData succeed\n");
367 
368                     if (Ret)
369                     {
370                         CcUnpinData(Bcb);
371                     }
372                 }
373             }
374         }
375     }
376 }
377 
378 
379 static
380 VOID
381 CleanupTest(
382     ULONG TestId,
383     PDEVICE_OBJECT DeviceObject)
384 {
385     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
386     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
387 
388     ok_eq_pointer(TestDeviceObject, DeviceObject);
389     ok_eq_ulong(TestTestId, TestId);
390 
391     if (!skip(TestFileObject != NULL, "No test FO\n"))
392     {
393         if (CcIsFileCached(TestFileObject))
394         {
395             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
396             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
397             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
398         }
399 
400         if (TestFileObject->FsContext != NULL)
401         {
402             ExFreePool(TestFileObject->FsContext);
403             TestFileObject->FsContext = NULL;
404             TestFileObject->SectionObjectPointer = NULL;
405         }
406 
407         ObDereferenceObject(TestFileObject);
408     }
409 
410     TestFileObject = NULL;
411     TestDeviceObject = NULL;
412     TestTestId = -1;
413 }
414 
415 
416 static
417 NTSTATUS
418 TestMessageHandler(
419     _In_ PDEVICE_OBJECT DeviceObject,
420     _In_ ULONG ControlCode,
421     _In_opt_ PVOID Buffer,
422     _In_ SIZE_T InLength,
423     _Inout_ PSIZE_T OutLength)
424 {
425     NTSTATUS Status = STATUS_SUCCESS;
426 
427     FsRtlEnterFileSystem();
428 
429     switch (ControlCode)
430     {
431         case IOCTL_START_TEST:
432             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
433             PerformTest(*(PULONG)Buffer, DeviceObject);
434             break;
435 
436         case IOCTL_FINISH_TEST:
437             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
438             CleanupTest(*(PULONG)Buffer, DeviceObject);
439             break;
440 
441         default:
442             Status = STATUS_NOT_IMPLEMENTED;
443             break;
444     }
445 
446     FsRtlExitFileSystem();
447 
448     return Status;
449 }
450 
451 static
452 NTSTATUS
453 TestIrpHandler(
454     _In_ PDEVICE_OBJECT DeviceObject,
455     _In_ PIRP Irp,
456     _In_ PIO_STACK_LOCATION IoStack)
457 {
458     NTSTATUS Status;
459 
460     PAGED_CODE();
461 
462     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
463     ASSERT(IoStack->MajorFunction == IRP_MJ_READ);
464 
465     FsRtlEnterFileSystem();
466 
467     Status = STATUS_NOT_SUPPORTED;
468     Irp->IoStatus.Information = 0;
469 
470     if (IoStack->MajorFunction == IRP_MJ_READ)
471     {
472         PMDL Mdl;
473         ULONG Length;
474         PVOID Buffer;
475         LARGE_INTEGER Offset;
476 
477         Offset = IoStack->Parameters.Read.ByteOffset;
478         Length = IoStack->Parameters.Read.Length;
479 
480         ok_eq_pointer(DeviceObject, TestDeviceObject);
481         ok_eq_pointer(IoStack->FileObject, TestFileObject);
482 
483         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
484 
485         ok_irql(APC_LEVEL);
486         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
487         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
488 
489         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
490         Buffer = MapAndLockUserBuffer(Irp, Length);
491         ok(Buffer != NULL, "Null pointer!\n");
492         RtlFillMemory(Buffer, Length, 0xBA);
493 
494         Status = STATUS_SUCCESS;
495         if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000)
496         {
497             *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE;
498         }
499 
500         Mdl = Irp->MdlAddress;
501         ok(Mdl != NULL, "Null pointer for MDL!\n");
502         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
503         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
504         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
505         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
506 
507         Irp->IoStatus.Information = Length;
508     }
509 
510     if (Status == STATUS_PENDING)
511     {
512         IoMarkIrpPending(Irp);
513         IoCompleteRequest(Irp, IO_NO_INCREMENT);
514         Status = STATUS_PENDING;
515     }
516     else
517     {
518         Irp->IoStatus.Status = Status;
519         IoCompleteRequest(Irp, IO_NO_INCREMENT);
520     }
521 
522     FsRtlExitFileSystem();
523 
524     return Status;
525 }
526