1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for CcPinMappedData 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     PULONG 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"CcPinMappedData";
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 PinInAnotherThread(IN PVOID Context)
151 {
152     BOOLEAN Ret;
153     PULONG Buffer;
154     PVOID Bcb, PinBcb;
155     LARGE_INTEGER Offset;
156     PTEST_CONTEXT TestContext;
157 
158     ok(TestFileObject != NULL, "Called in invalid context!\n");
159     ok_eq_ulong(TestTestId, 4);
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 = 0;
169 
170     KmtStartSeh();
171     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
172     KmtEndSeh(STATUS_SUCCESS);
173 
174     if (!skip(Ret == TRUE, "CcMapData failed\n"))
175     {
176         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
177         ok_eq_pointer(Buffer, TestContext->Buffer);
178 
179         Ret = FALSE;
180         PinBcb = Bcb;
181 
182         KmtStartSeh();
183         Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, 0, &PinBcb);
184         KmtEndSeh(STATUS_SUCCESS);
185 
186         if (!skip(Ret == TRUE, "CcPinMappedData failed\n"))
187         {
188             ok(Bcb != PinBcb, "Returned same BCB!\n");
189             ok_eq_pointer(PinBcb, TestContext->Bcb);
190 
191             Bcb = PinBcb;
192         }
193 
194         CcUnpinData(Bcb);
195     }
196 }
197 
198 static
199 VOID
200 NTAPI
201 PinInAnotherThreadExclusive(IN PVOID Context)
202 {
203     BOOLEAN Ret;
204     PULONG Buffer;
205     PVOID Bcb, PinBcb;
206     LARGE_INTEGER Offset;
207     PTEST_CONTEXT TestContext;
208 
209     ok(TestFileObject != NULL, "Called in invalid context!\n");
210     ok_eq_ulong(TestTestId, 2);
211 
212     TestContext = Context;
213     ok(TestContext != NULL, "Called in invalid context!\n");
214     ok(TestContext->Bcb != NULL, "Called in invalid context!\n");
215     ok(TestContext->Buffer != NULL, "Called in invalid context!\n");
216     ok(TestContext->Length != 0, "Called in invalid context!\n");
217 
218     Ret = FALSE;
219     Offset.QuadPart = 0;
220 
221     KmtStartSeh();
222     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
223     KmtEndSeh(STATUS_SUCCESS);
224 
225     if (!skip(Ret == TRUE, "CcMapData failed\n"))
226     {
227         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
228         ok_eq_pointer(Buffer, TestContext->Buffer);
229 
230         Ret = FALSE;
231         PinBcb = Bcb;
232 
233         KmtStartSeh();
234         Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_EXCLUSIVE, &PinBcb);
235         KmtEndSeh(STATUS_SUCCESS);
236 
237         if (!skip(Ret == FALSE, "CcPinMappedData succeed\n"))
238         {
239             ok_eq_pointer(PinBcb, Bcb);
240 
241             Ret = FALSE;
242             PinBcb = Bcb;
243 
244             KmtStartSeh();
245             Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, 0, &PinBcb);
246             KmtEndSeh(STATUS_SUCCESS);
247 
248             if (!skip(Ret == FALSE, "CcPinMappedData succeed\n"))
249             {
250                 ok_eq_pointer(PinBcb, Bcb);
251             }
252             else
253             {
254                 Bcb = PinBcb;
255             }
256         }
257         else
258         {
259             Bcb = PinBcb;
260         }
261 
262         CcUnpinData(Bcb);
263     }
264 }
265 
266 static
267 VOID
268 PerformTest(
269     ULONG TestId,
270     PDEVICE_OBJECT DeviceObject)
271 {
272     PVOID Bcb, PinBcb, NewBcb;
273     BOOLEAN Ret;
274     PULONG Buffer;
275     PTEST_FCB Fcb;
276     LARGE_INTEGER Offset;
277 
278     ok_eq_pointer(TestFileObject, NULL);
279     ok_eq_pointer(TestDeviceObject, NULL);
280     ok_eq_ulong(TestTestId, -1);
281 
282     TestDeviceObject = DeviceObject;
283     TestTestId = TestId;
284     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
285     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
286     {
287         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
288         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
289         {
290             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
291             ExInitializeFastMutex(&Fcb->HeaderMutex);
292             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
293 
294             TestFileObject->FsContext = Fcb;
295             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
296 
297             KmtStartSeh();
298             CcInitializeCacheMap(TestFileObject, &FileSizes, TRUE, &Callbacks, NULL);
299             KmtEndSeh(STATUS_SUCCESS);
300 
301             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
302             {
303                 if (TestId == 0)
304                 {
305                     Ret = FALSE;
306                     Offset.QuadPart = 0;
307                     KmtStartSeh();
308                     Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
309                     KmtEndSeh(STATUS_SUCCESS);
310 
311                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
312                     {
313                         Ret = FALSE;
314                         PinBcb = Bcb;
315                         ok_eq_ulong(Buffer[0x3000 / sizeof(ULONG)], 0xDEADBABE);
316 
317                         KmtStartSeh();
318                         Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &PinBcb);
319                         KmtEndSeh(STATUS_SUCCESS);
320 
321                         if (!skip(Ret == TRUE, "CcPinMappedData failed\n"))
322                         {
323                             ok(Bcb != PinBcb, "Returned same BCB!\n");
324                             ok(*(PUSHORT)PinBcb == 0x2FD, "Not a BCB: %x\n", *(PUSHORT)PinBcb);
325 
326                             /* Previous BCB isn't valid anymore! */
327                             Bcb = PinBcb;
328                         }
329 
330                         CcUnpinData(Bcb);
331                     }
332                 }
333                 else if (TestId == 1)
334                 {
335                     Ret = FALSE;
336                     Offset.QuadPart = 0;
337                     KmtStartSeh();
338                     Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &PinBcb, (PVOID *)&Buffer);
339                     KmtEndSeh(STATUS_SUCCESS);
340 
341                     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
342                     {
343                         ok(*(PUSHORT)PinBcb == 0x2FD, "Not a BCB: %x\n", *(PUSHORT)PinBcb);
344                         ok_eq_ulong(Buffer[0x3000 / sizeof(ULONG)], 0xDEADBABE);
345 
346                         Ret = FALSE;
347                         KmtStartSeh();
348                         Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
349                         KmtEndSeh(STATUS_SUCCESS);
350 
351                         if (!skip(Ret == TRUE, "CcMapData failed\n"))
352                         {
353                             ok(Bcb != PinBcb, "Returned same BCB!\n");
354 
355                             Ret = FALSE;
356                             NewBcb = Bcb;
357                             KmtStartSeh();
358                             Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &NewBcb);
359                             KmtEndSeh(STATUS_SUCCESS);
360 
361                             if (!skip(Ret == TRUE, "CcPinMappedData failed\n"))
362                             {
363                                 ok(Bcb != NewBcb, "Returned same BCB!\n");
364                                 ok_eq_pointer(NewBcb, PinBcb);
365                                 ok(*(PUSHORT)NewBcb == 0x2FD, "Not a BCB: %x\n", *(PUSHORT)NewBcb);
366 
367                                 /* Previous BCB isn't valid anymore! */
368                                 Bcb = NewBcb;
369                             }
370 
371                             CcUnpinData(Bcb);
372                         }
373 
374                         CcUnpinData(PinBcb);
375                     }
376                 }
377                 else if (TestId == 2)
378                 {
379                     PTEST_CONTEXT TestContext;
380 
381                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
382                     if (!skip(TestContext != NULL, "ExAllocatePool failed\n"))
383                     {
384                         Ret = FALSE;
385                         Offset.QuadPart = 0;
386                         KmtStartSeh();
387                         Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT | PIN_EXCLUSIVE, &TestContext->Bcb, (PVOID *)&TestContext->Buffer);
388                         KmtEndSeh(STATUS_SUCCESS);
389 
390                         if (!skip(Ret == TRUE, "CcPinRead failed\n"))
391                         {
392                             PKTHREAD ThreadHandle;
393 
394                             ok(*(PUSHORT)TestContext->Bcb == 0x2FD, "Not a BCB: %x\n", *(PUSHORT)TestContext->Bcb);
395                             ok_eq_ulong(TestContext->Buffer[0x3000 / sizeof(ULONG)], 0xDEADBABE);
396 
397                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
398                             ThreadHandle = KmtStartThread(PinInAnotherThreadExclusive, TestContext);
399                             KmtFinishThread(ThreadHandle, NULL);
400 
401                             CcUnpinData(TestContext->Bcb);
402                         }
403 
404                         ExFreePool(TestContext);
405                     }
406                 }
407                 else if (TestId == 3)
408                 {
409                     Ret = FALSE;
410                     Offset.QuadPart = 0;
411                     KmtStartSeh();
412                     Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
413                     KmtEndSeh(STATUS_SUCCESS);
414 
415                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
416                     {
417                         Ret = FALSE;
418                         PinBcb = Bcb;
419                         ok_eq_ulong(Buffer[0x3000 / sizeof(ULONG)], 0xDEADBABE);
420 
421                         KmtStartSeh();
422                         Ret = CcPinMappedData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_IF_BCB, &PinBcb);
423                         KmtEndSeh(STATUS_SUCCESS);
424 
425                         if (!skip(Ret == FALSE, "CcPinMappedData succeed\n"))
426                         {
427                             ok_eq_pointer(Bcb, PinBcb);
428                         }
429                         else
430                         {
431                             Bcb = PinBcb;
432                         }
433 
434                         CcUnpinData(Bcb);
435                     }
436                 }
437                 else if (TestId == 4)
438                 {
439                     PTEST_CONTEXT TestContext;
440 
441                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
442                     if (!skip(TestContext != NULL, "ExAllocatePool failed\n"))
443                     {
444                         Ret = FALSE;
445                         Offset.QuadPart = 0;
446                         KmtStartSeh();
447                         Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &TestContext->Bcb, (PVOID *)&TestContext->Buffer);
448                         KmtEndSeh(STATUS_SUCCESS);
449 
450                         if (!skip(Ret == TRUE, "CcPinRead failed\n"))
451                         {
452                             PKTHREAD ThreadHandle;
453 
454                             ok(*(PUSHORT)TestContext->Bcb == 0x2FD, "Not a BCB: %x\n", *(PUSHORT)TestContext->Bcb);
455                             ok_eq_ulong(TestContext->Buffer[0x3000 / sizeof(ULONG)], 0xDEADBABE);
456 
457                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
458                             ThreadHandle = KmtStartThread(PinInAnotherThread, TestContext);
459                             KmtFinishThread(ThreadHandle, NULL);
460 
461                             CcUnpinData(TestContext->Bcb);
462                         }
463 
464                         ExFreePool(TestContext);
465                     }
466                 }
467             }
468         }
469     }
470 }
471 
472 
473 static
474 VOID
475 CleanupTest(
476     ULONG TestId,
477     PDEVICE_OBJECT DeviceObject)
478 {
479     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
480     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
481 
482     ok_eq_pointer(TestDeviceObject, DeviceObject);
483     ok_eq_ulong(TestTestId, TestId);
484 
485     if (!skip(TestFileObject != NULL, "No test FO\n"))
486     {
487         if (CcIsFileCached(TestFileObject))
488         {
489             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
490             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
491             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
492         }
493 
494         if (TestFileObject->FsContext != NULL)
495         {
496             ExFreePool(TestFileObject->FsContext);
497             TestFileObject->FsContext = NULL;
498             TestFileObject->SectionObjectPointer = NULL;
499         }
500 
501         ObDereferenceObject(TestFileObject);
502     }
503 
504     TestFileObject = NULL;
505     TestDeviceObject = NULL;
506     TestTestId = -1;
507 }
508 
509 
510 static
511 NTSTATUS
512 TestMessageHandler(
513     _In_ PDEVICE_OBJECT DeviceObject,
514     _In_ ULONG ControlCode,
515     _In_opt_ PVOID Buffer,
516     _In_ SIZE_T InLength,
517     _Inout_ PSIZE_T OutLength)
518 {
519     NTSTATUS Status = STATUS_SUCCESS;
520 
521     FsRtlEnterFileSystem();
522 
523     switch (ControlCode)
524     {
525         case IOCTL_START_TEST:
526             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
527             PerformTest(*(PULONG)Buffer, DeviceObject);
528             break;
529 
530         case IOCTL_FINISH_TEST:
531             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
532             CleanupTest(*(PULONG)Buffer, DeviceObject);
533             break;
534 
535         default:
536             Status = STATUS_NOT_IMPLEMENTED;
537             break;
538     }
539 
540     FsRtlExitFileSystem();
541 
542     return Status;
543 }
544 
545 static
546 NTSTATUS
547 TestIrpHandler(
548     _In_ PDEVICE_OBJECT DeviceObject,
549     _In_ PIRP Irp,
550     _In_ PIO_STACK_LOCATION IoStack)
551 {
552     NTSTATUS Status;
553 
554     PAGED_CODE();
555 
556     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
557     ASSERT(IoStack->MajorFunction == IRP_MJ_READ);
558 
559     FsRtlEnterFileSystem();
560 
561     Status = STATUS_NOT_SUPPORTED;
562     Irp->IoStatus.Information = 0;
563 
564     if (IoStack->MajorFunction == IRP_MJ_READ)
565     {
566         PMDL Mdl;
567         ULONG Length;
568         PVOID Buffer;
569         LARGE_INTEGER Offset;
570 
571         Offset = IoStack->Parameters.Read.ByteOffset;
572         Length = IoStack->Parameters.Read.Length;
573 
574         ok_eq_pointer(DeviceObject, TestDeviceObject);
575         ok_eq_pointer(IoStack->FileObject, TestFileObject);
576 
577         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
578 
579         ok_irql(APC_LEVEL);
580         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
581         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
582 
583         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
584         Buffer = MapAndLockUserBuffer(Irp, Length);
585         ok(Buffer != NULL, "Null pointer!\n");
586         RtlFillMemory(Buffer, Length, 0xBA);
587 
588         Status = STATUS_SUCCESS;
589         if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000)
590         {
591             *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE;
592         }
593 
594         Mdl = Irp->MdlAddress;
595         ok(Mdl != NULL, "Null pointer for MDL!\n");
596         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
597         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
598         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
599         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
600 
601         Irp->IoStatus.Information = Length;
602     }
603 
604     if (Status == STATUS_PENDING)
605     {
606         IoMarkIrpPending(Irp);
607         IoCompleteRequest(Irp, IO_NO_INCREMENT);
608         Status = STATUS_PENDING;
609     }
610     else
611     {
612         Irp->IoStatus.Status = Status;
613         IoCompleteRequest(Irp, IO_NO_INCREMENT);
614     }
615 
616     FsRtlExitFileSystem();
617 
618     return Status;
619 }
620