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     return;
216 }
217 
218 static
219 VOID
220 PerformTest(
221     ULONG TestId,
222     PDEVICE_OBJECT DeviceObject)
223 {
224     PVOID Bcb;
225     BOOLEAN Ret;
226     PULONG Buffer;
227     PTEST_FCB Fcb;
228     LARGE_INTEGER Offset;
229 
230     ok_eq_pointer(TestFileObject, NULL);
231     ok_eq_pointer(TestDeviceObject, NULL);
232     ok_eq_ulong(TestTestId, -1);
233 
234     TestDeviceObject = DeviceObject;
235     TestTestId = TestId;
236     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
237     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
238     {
239         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
240         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
241         {
242             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
243             ExInitializeFastMutex(&Fcb->HeaderMutex);
244             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
245 
246             TestFileObject->FsContext = Fcb;
247             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
248 
249             KmtStartSeh();
250             CcInitializeCacheMap(TestFileObject, &FileSizes, FALSE, &Callbacks, NULL);
251             KmtEndSeh(STATUS_SUCCESS);
252 
253             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
254             {
255                 if (TestId < 3)
256                 {
257                     Ret = FALSE;
258                     Offset.QuadPart = TestId * 0x1000;
259                     KmtStartSeh();
260                     Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
261                     KmtEndSeh(STATUS_SUCCESS);
262 
263                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
264                     {
265                         ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE);
266 
267                         CcUnpinData(Bcb);
268                     }
269                 }
270                 else if (TestId == 3)
271                 {
272                     PTEST_CONTEXT TestContext;
273 
274                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
275                     if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
276                     {
277                         Ret = FALSE;
278                         Offset.QuadPart = 0x1000;
279                         KmtStartSeh();
280                         Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &TestContext->Bcb, &TestContext->Buffer);
281                         KmtEndSeh(STATUS_SUCCESS);
282 
283                         if (!skip(Ret == TRUE, "CcMapData failed\n"))
284                         {
285                             PKTHREAD ThreadHandle;
286 
287 #ifdef _X86_
288                             /* FIXME: Should be fixed, will fail under certains conditions */
289                             ok(TestContext->Buffer > (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF,
290                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
291 #else
292 #ifdef _M_AMD64
293                             ok(TestContext->Buffer > (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000,
294                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
295 #else
296                             skip(FALSE, "System space mapping not defined\n");
297 #endif
298 #endif
299 
300                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
301                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
302                             KmtFinishThread(ThreadHandle, NULL);
303 
304                             TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart;
305                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
306                             KmtFinishThread(ThreadHandle, NULL);
307 
308                             CcUnpinData(TestContext->Bcb);
309                         }
310 
311                         ExFreePool(TestContext);
312                     }
313                 }
314             }
315         }
316     }
317 }
318 
319 
320 static
321 VOID
322 CleanupTest(
323     ULONG TestId,
324     PDEVICE_OBJECT DeviceObject)
325 {
326     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
327     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
328 
329     ok_eq_pointer(TestDeviceObject, DeviceObject);
330     ok_eq_ulong(TestTestId, TestId);
331 
332     if (!skip(TestFileObject != NULL, "No test FO\n"))
333     {
334         if (CcIsFileCached(TestFileObject))
335         {
336             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
337             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
338             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
339         }
340 
341         if (TestFileObject->FsContext != NULL)
342         {
343             ExFreePool(TestFileObject->FsContext);
344             TestFileObject->FsContext = NULL;
345             TestFileObject->SectionObjectPointer = NULL;
346         }
347 
348         ObDereferenceObject(TestFileObject);
349     }
350 
351     TestFileObject = NULL;
352     TestDeviceObject = NULL;
353     TestTestId = -1;
354 }
355 
356 
357 static
358 NTSTATUS
359 TestMessageHandler(
360     _In_ PDEVICE_OBJECT DeviceObject,
361     _In_ ULONG ControlCode,
362     _In_opt_ PVOID Buffer,
363     _In_ SIZE_T InLength,
364     _Inout_ PSIZE_T OutLength)
365 {
366     NTSTATUS Status = STATUS_SUCCESS;
367 
368     FsRtlEnterFileSystem();
369 
370     switch (ControlCode)
371     {
372         case IOCTL_START_TEST:
373             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
374             PerformTest(*(PULONG)Buffer, DeviceObject);
375             break;
376 
377         case IOCTL_FINISH_TEST:
378             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
379             CleanupTest(*(PULONG)Buffer, DeviceObject);
380             break;
381 
382         default:
383             Status = STATUS_NOT_IMPLEMENTED;
384             break;
385     }
386 
387     FsRtlExitFileSystem();
388 
389     return Status;
390 }
391 
392 static
393 NTSTATUS
394 TestIrpHandler(
395     _In_ PDEVICE_OBJECT DeviceObject,
396     _In_ PIRP Irp,
397     _In_ PIO_STACK_LOCATION IoStack)
398 {
399     NTSTATUS Status;
400 
401     PAGED_CODE();
402 
403     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
404     ASSERT(IoStack->MajorFunction == IRP_MJ_READ);
405 
406     FsRtlEnterFileSystem();
407 
408     Status = STATUS_NOT_SUPPORTED;
409     Irp->IoStatus.Information = 0;
410 
411     if (IoStack->MajorFunction == IRP_MJ_READ)
412     {
413         PMDL Mdl;
414         ULONG Length;
415         PVOID Buffer;
416         LARGE_INTEGER Offset;
417 
418         Offset = IoStack->Parameters.Read.ByteOffset;
419         Length = IoStack->Parameters.Read.Length;
420 
421         ok_eq_pointer(DeviceObject, TestDeviceObject);
422         ok_eq_pointer(IoStack->FileObject, TestFileObject);
423 
424         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
425 
426         ok_irql(APC_LEVEL);
427         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
428         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
429 
430         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
431         Buffer = MapAndLockUserBuffer(Irp, Length);
432         ok(Buffer != NULL, "Null pointer!\n");
433         RtlFillMemory(Buffer, Length, 0xBA);
434 
435         Status = STATUS_SUCCESS;
436         if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000)
437         {
438             *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE;
439         }
440 
441         Mdl = Irp->MdlAddress;
442         ok(Mdl != NULL, "Null pointer for MDL!\n");
443         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
444         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
445         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
446         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
447 
448         Irp->IoStatus.Information = Length;
449     }
450 
451     if (Status == STATUS_PENDING)
452     {
453         IoMarkIrpPending(Irp);
454         IoCompleteRequest(Irp, IO_NO_INCREMENT);
455         Status = STATUS_PENDING;
456     }
457     else
458     {
459         Irp->IoStatus.Status = Status;
460         IoCompleteRequest(Irp, IO_NO_INCREMENT);
461     }
462 
463     FsRtlExitFileSystem();
464 
465     return Status;
466 }
467