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 static ULONGLONG Memory = 0;
36 static BOOLEAN TS = FALSE;
37 
38 NTSTATUS
39 TestEntry(
40     _In_ PDRIVER_OBJECT DriverObject,
41     _In_ PCUNICODE_STRING RegistryPath,
42     _Out_ PCWSTR *DeviceName,
43     _Inout_ INT *Flags)
44 {
45     ULONG Length;
46     SYSTEM_BASIC_INFORMATION SBI;
47     NTSTATUS Status = STATUS_SUCCESS;
48     RTL_OSVERSIONINFOEXW VersionInfo;
49 
50     PAGED_CODE();
51 
52     UNREFERENCED_PARAMETER(RegistryPath);
53 
54     *DeviceName = L"CcMapData";
55     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
56              TESTENTRY_BUFFERED_IO_DEVICE |
57              TESTENTRY_NO_READONLY_DEVICE;
58 
59     KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
60     KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
61 
62     Status = ZwQuerySystemInformation(SystemBasicInformation,
63                                       &SBI,
64                                       sizeof(SBI),
65                                       &Length);
66     if (NT_SUCCESS(Status))
67     {
68         Memory = (SBI.NumberOfPhysicalPages * SBI.PageSize) / 1024 / 1024;
69     }
70     else
71     {
72         Status = STATUS_SUCCESS;
73     }
74 
75     VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
76     Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&VersionInfo);
77     if (NT_SUCCESS(Status))
78     {
79         TS = BooleanFlagOn(VersionInfo.wSuiteMask, VER_SUITE_TERMINAL) &&
80              !BooleanFlagOn(VersionInfo.wSuiteMask, VER_SUITE_SINGLEUSERTS);
81     }
82     else
83     {
84         Status = STATUS_SUCCESS;
85     }
86 
87     trace("System with %I64dMb RAM and terminal services %S\n",  Memory, (TS ? L"enabled" : L"disabled"));
88 
89     return Status;
90 }
91 
92 VOID
93 TestUnload(
94     _In_ PDRIVER_OBJECT DriverObject)
95 {
96     PAGED_CODE();
97 }
98 
99 BOOLEAN
100 NTAPI
101 AcquireForLazyWrite(
102     _In_ PVOID Context,
103     _In_ BOOLEAN Wait)
104 {
105     return TRUE;
106 }
107 
108 VOID
109 NTAPI
110 ReleaseFromLazyWrite(
111     _In_ PVOID Context)
112 {
113     return;
114 }
115 
116 BOOLEAN
117 NTAPI
118 AcquireForReadAhead(
119     _In_ PVOID Context,
120     _In_ BOOLEAN Wait)
121 {
122     return TRUE;
123 }
124 
125 VOID
126 NTAPI
127 ReleaseFromReadAhead(
128     _In_ PVOID Context)
129 {
130     return;
131 }
132 
133 static CACHE_MANAGER_CALLBACKS Callbacks = {
134     AcquireForLazyWrite,
135     ReleaseFromLazyWrite,
136     AcquireForReadAhead,
137     ReleaseFromReadAhead,
138 };
139 
140 static CC_FILE_SIZES FileSizes = {
141     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .AllocationSize
142     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .FileSize
143     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000)  // .ValidDataLength
144 };
145 
146 static
147 PVOID
148 MapAndLockUserBuffer(
149     _In_ _Out_ PIRP Irp,
150     _In_ ULONG BufferLength)
151 {
152     PMDL Mdl;
153 
154     if (Irp->MdlAddress == NULL)
155     {
156         Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
157         if (Mdl == NULL)
158         {
159             return NULL;
160         }
161 
162         _SEH2_TRY
163         {
164             MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess);
165         }
166         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
167         {
168             IoFreeMdl(Mdl);
169             Irp->MdlAddress = NULL;
170             _SEH2_YIELD(return NULL);
171         }
172         _SEH2_END;
173     }
174 
175     return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
176 }
177 
178 static
179 VOID
180 NTAPI
181 MapInAnotherThread(IN PVOID Context)
182 {
183     BOOLEAN Ret;
184     PULONG Buffer;
185     PVOID Bcb;
186     LARGE_INTEGER Offset;
187     PTEST_CONTEXT TestContext;
188 
189     ok(TestFileObject != NULL, "Called in invalid context!\n");
190     ok_eq_ulong(TestTestId, 3);
191 
192     TestContext = Context;
193     ok(TestContext != NULL, "Called in invalid context!\n");
194     ok(TestContext->Bcb != NULL, "Called in invalid context!\n");
195     ok(TestContext->Buffer != NULL, "Called in invalid context!\n");
196     ok(TestContext->Length != 0, "Called in invalid context!\n");
197 
198     Ret = FALSE;
199     Offset.QuadPart = 0x1000;
200     KmtStartSeh();
201     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
202     KmtEndSeh(STATUS_SUCCESS);
203 
204     if (!skip(Ret == TRUE, "CcMapData failed\n"))
205     {
206         ok_eq_pointer(Bcb, TestContext->Bcb);
207         ok_eq_pointer(Buffer, TestContext->Buffer);
208 
209         CcUnpinData(Bcb);
210     }
211 
212     KmtStartSeh();
213     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer);
214     KmtEndSeh(STATUS_SUCCESS);
215 
216     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
217     {
218         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
219         ok_eq_pointer(Buffer, TestContext->Buffer);
220 
221         CcUnpinData(Bcb);
222     }
223 
224     KmtStartSeh();
225     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
226     KmtEndSeh(STATUS_SUCCESS);
227     ok(Ret == FALSE, "CcPinRead succeed\n");
228 
229     if (Ret)
230     {
231         CcUnpinData(Bcb);
232     }
233 
234     KmtStartSeh();
235     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer);
236     KmtEndSeh(STATUS_SUCCESS);
237 
238     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
239     {
240         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
241         ok_eq_pointer(Buffer, TestContext->Buffer);
242 
243         CcUnpinData(Bcb);
244     }
245 
246     Offset.QuadPart = 0x1500;
247     TestContext->Length -= 0x500;
248 
249     KmtStartSeh();
250     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
251     KmtEndSeh(STATUS_SUCCESS);
252 
253     if (!skip(Ret == TRUE, "CcMapData failed\n"))
254     {
255         ok_eq_pointer(Bcb, TestContext->Bcb);
256         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
257 
258         CcUnpinData(Bcb);
259     }
260 
261     return;
262 }
263 
264 static
265 VOID
266 PerformTest(
267     ULONG TestId,
268     PDEVICE_OBJECT DeviceObject)
269 {
270     PVOID Bcb;
271     BOOLEAN Ret;
272     PULONG Buffer;
273     PTEST_FCB Fcb;
274     LARGE_INTEGER Offset;
275 
276     ok_eq_pointer(TestFileObject, NULL);
277     ok_eq_pointer(TestDeviceObject, NULL);
278     ok_eq_ulong(TestTestId, -1);
279 
280     TestDeviceObject = DeviceObject;
281     TestTestId = TestId;
282     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
283     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
284     {
285         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
286         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
287         {
288             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
289             ExInitializeFastMutex(&Fcb->HeaderMutex);
290             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
291 
292             TestFileObject->FsContext = Fcb;
293             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
294 
295             KmtStartSeh();
296             CcInitializeCacheMap(TestFileObject, &FileSizes, FALSE, &Callbacks, NULL);
297             KmtEndSeh(STATUS_SUCCESS);
298 
299             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
300             {
301                 if (TestId < 3)
302                 {
303                     Ret = FALSE;
304                     Offset.QuadPart = TestId * 0x1000;
305                     KmtStartSeh();
306                     Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
307                     KmtEndSeh(STATUS_SUCCESS);
308 
309                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
310                     {
311                         ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE);
312 
313                         CcUnpinData(Bcb);
314                     }
315                 }
316                 else if (TestId == 3)
317                 {
318                     PTEST_CONTEXT TestContext;
319 
320                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
321                     if (!skip(TestContext != NULL, "ExAllocatePool failed\n"))
322                     {
323                         Ret = FALSE;
324                         Offset.QuadPart = 0x1000;
325                         KmtStartSeh();
326                         Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &TestContext->Bcb, &TestContext->Buffer);
327                         KmtEndSeh(STATUS_SUCCESS);
328 
329                         if (!skip(Ret == TRUE, "CcMapData failed\n"))
330                         {
331                             PKTHREAD ThreadHandle;
332 
333                             /* That's a bit rough but should do the job */
334 #ifdef _X86_
335                             if (Memory >= 2 * 1024)
336                             {
337                                 ok((TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xE0FFFFFF) ||
338                                    (TestContext->Buffer >= (PVOID)0xA4000000 && TestContext->Buffer < (PVOID)0xBFFFFFFF),
339                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
340                             }
341                             else if (TS)
342                             {
343                                 ok(TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF,
344                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
345                             }
346                             else
347                             {
348                                 ok(TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDBFFFFFF,
349                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
350                             }
351 #elif defined(_M_AMD64)
352                             ok(TestContext->Buffer >= (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000,
353                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
354 #else
355                             skip(FALSE, "System space mapping not defined\n");
356 #endif
357 
358                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
359                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
360                             KmtFinishThread(ThreadHandle, NULL);
361 
362                             TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart;
363                             ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext);
364                             KmtFinishThread(ThreadHandle, NULL);
365 
366                             CcUnpinData(TestContext->Bcb);
367                         }
368 
369                         ExFreePool(TestContext);
370                     }
371                 }
372                 else if (TestId == 4)
373                 {
374                     FileSizes.AllocationSize.QuadPart += VACB_MAPPING_GRANULARITY;
375                     CcSetFileSizes(TestFileObject, &FileSizes);
376 
377                     /* Map after EOF */
378                     Ret = FALSE;
379                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000;
380 
381                     KmtStartSeh();
382                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
383                     KmtEndSeh(STATUS_SUCCESS);
384                     ok(Ret == TRUE, "CcMapData failed\n");
385 
386                     if (Ret)
387                     {
388                         CcUnpinData(Bcb);
389                     }
390 
391                     /* Map a VACB after EOF. */
392                     Ret = FALSE;
393                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY;
394 
395                     KmtStartSeh();
396                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
397                     KmtEndSeh(STATUS_SUCCESS);
398                     ok(Ret == TRUE, "CcMapData failed\n");
399 
400                     if (Ret)
401                     {
402                         CcUnpinData(Bcb);
403                     }
404 
405                     /* Map after Allocation */
406                     Ret = FALSE;
407                     Offset.QuadPart = FileSizes.AllocationSize.QuadPart + 0x1000;
408 
409                     KmtStartSeh();
410                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
411                     KmtEndSeh(STATUS_SUCCESS);
412                     ok(Ret == TRUE, "CcMapData failed\n");
413 
414                     if (Ret)
415                     {
416                         CcUnpinData(Bcb);
417                     }
418 
419                     Ret = FALSE;
420                     Offset.QuadPart = FileSizes.AllocationSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY;
421 
422                     KmtStartSeh();
423                     Ret = CcMapData(TestFileObject, &Offset, 0x1000, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
424                     KmtEndSeh(STATUS_SUCCESS);
425                     ok(Ret == TRUE, "CcMapData failed\n");
426 
427                     if (Ret)
428                     {
429                         CcUnpinData(Bcb);
430                     }
431 
432                     /* Map more than a VACB */
433                     Ret = FALSE;
434                     Offset.QuadPart = 0x0;
435 
436                     KmtStartSeh();
437                     Ret = CcMapData(TestFileObject, &Offset, 0x1000 + VACB_MAPPING_GRANULARITY, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
438                     KmtEndSeh(STATUS_SUCCESS);
439                     ok(Ret == TRUE, "CcMapData failed\n");
440 
441                     if (Ret)
442                     {
443                         CcUnpinData(Bcb);
444                     }
445                 }
446             }
447         }
448     }
449 }
450 
451 
452 static
453 VOID
454 CleanupTest(
455     ULONG TestId,
456     PDEVICE_OBJECT DeviceObject)
457 {
458     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
459     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
460 
461     ok_eq_pointer(TestDeviceObject, DeviceObject);
462     ok_eq_ulong(TestTestId, TestId);
463 
464     if (!skip(TestFileObject != NULL, "No test FO\n"))
465     {
466         if (CcIsFileCached(TestFileObject))
467         {
468             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
469             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
470             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
471         }
472 
473         if (TestFileObject->FsContext != NULL)
474         {
475             ExFreePool(TestFileObject->FsContext);
476             TestFileObject->FsContext = NULL;
477             TestFileObject->SectionObjectPointer = NULL;
478         }
479 
480         ObDereferenceObject(TestFileObject);
481     }
482 
483     TestFileObject = NULL;
484     TestDeviceObject = NULL;
485     TestTestId = -1;
486 }
487 
488 
489 static
490 NTSTATUS
491 TestMessageHandler(
492     _In_ PDEVICE_OBJECT DeviceObject,
493     _In_ ULONG ControlCode,
494     _In_opt_ PVOID Buffer,
495     _In_ SIZE_T InLength,
496     _Inout_ PSIZE_T OutLength)
497 {
498     NTSTATUS Status = STATUS_SUCCESS;
499 
500     FsRtlEnterFileSystem();
501 
502     switch (ControlCode)
503     {
504         case IOCTL_START_TEST:
505             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
506             PerformTest(*(PULONG)Buffer, DeviceObject);
507             break;
508 
509         case IOCTL_FINISH_TEST:
510             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
511             CleanupTest(*(PULONG)Buffer, DeviceObject);
512             break;
513 
514         default:
515             Status = STATUS_NOT_IMPLEMENTED;
516             break;
517     }
518 
519     FsRtlExitFileSystem();
520 
521     return Status;
522 }
523 
524 static
525 NTSTATUS
526 TestIrpHandler(
527     _In_ PDEVICE_OBJECT DeviceObject,
528     _In_ PIRP Irp,
529     _In_ PIO_STACK_LOCATION IoStack)
530 {
531     NTSTATUS Status;
532 
533     PAGED_CODE();
534 
535     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
536     ASSERT(IoStack->MajorFunction == IRP_MJ_READ);
537 
538     FsRtlEnterFileSystem();
539 
540     Status = STATUS_NOT_SUPPORTED;
541     Irp->IoStatus.Information = 0;
542 
543     if (IoStack->MajorFunction == IRP_MJ_READ)
544     {
545         PMDL Mdl;
546         ULONG Length;
547         PVOID Buffer;
548         LARGE_INTEGER Offset;
549 
550         Offset = IoStack->Parameters.Read.ByteOffset;
551         Length = IoStack->Parameters.Read.Length;
552 
553         ok_eq_pointer(DeviceObject, TestDeviceObject);
554         ok_eq_pointer(IoStack->FileObject, TestFileObject);
555 
556         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
557 
558         ok_irql(APC_LEVEL);
559         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
560         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
561 
562         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
563         Buffer = MapAndLockUserBuffer(Irp, Length);
564         ok(Buffer != NULL, "Null pointer!\n");
565         RtlFillMemory(Buffer, Length, 0xBA);
566 
567         Status = STATUS_SUCCESS;
568         if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000)
569         {
570             *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE;
571         }
572 
573         Mdl = Irp->MdlAddress;
574         ok(Mdl != NULL, "Null pointer for MDL!\n");
575         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
576         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
577         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
578         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
579 
580         Irp->IoStatus.Information = Length;
581     }
582 
583     if (Status == STATUS_PENDING)
584     {
585         IoMarkIrpPending(Irp);
586         IoCompleteRequest(Irp, IO_NO_INCREMENT);
587         Status = STATUS_PENDING;
588     }
589     else
590     {
591         Irp->IoStatus.Status = Status;
592         IoCompleteRequest(Irp, IO_NO_INCREMENT);
593     }
594 
595     FsRtlExitFileSystem();
596 
597     return Status;
598 }
599