1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for CcPinRead 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 BOOLEAN TestWriteCalled = FALSE;
36 static ULONGLONG Memory = 0;
37 static BOOLEAN TS = FALSE;
38 
39 LARGE_INTEGER WriteOffset;
40 ULONG WriteLength;
41 
42 NTSTATUS
43 TestEntry(
44     _In_ PDRIVER_OBJECT DriverObject,
45     _In_ PCUNICODE_STRING RegistryPath,
46     _Out_ PCWSTR *DeviceName,
47     _Inout_ INT *Flags)
48 {
49     ULONG Length;
50     SYSTEM_BASIC_INFORMATION SBI;
51     NTSTATUS Status = STATUS_SUCCESS;
52     RTL_OSVERSIONINFOEXW VersionInfo;
53 
54     PAGED_CODE();
55 
56     UNREFERENCED_PARAMETER(RegistryPath);
57 
58     *DeviceName = L"CcPinRead";
59     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
60              TESTENTRY_BUFFERED_IO_DEVICE |
61              TESTENTRY_NO_READONLY_DEVICE;
62 
63     KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
64     KmtRegisterIrpHandler(IRP_MJ_WRITE, NULL, TestIrpHandler);
65     KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
66 
67     Status = ZwQuerySystemInformation(SystemBasicInformation,
68                                       &SBI,
69                                       sizeof(SBI),
70                                       &Length);
71     if (NT_SUCCESS(Status))
72     {
73         Memory = (SBI.NumberOfPhysicalPages * SBI.PageSize) / 1024 / 1024;
74     }
75     else
76     {
77         Status = STATUS_SUCCESS;
78     }
79 
80     VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
81     Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&VersionInfo);
82     if (NT_SUCCESS(Status))
83     {
84         TS = BooleanFlagOn(VersionInfo.wSuiteMask, VER_SUITE_TERMINAL) &&
85              !BooleanFlagOn(VersionInfo.wSuiteMask, VER_SUITE_SINGLEUSERTS);
86     }
87     else
88     {
89         Status = STATUS_SUCCESS;
90     }
91 
92     trace("System with %I64dMb RAM and terminal services %S\n",  Memory, (TS ? L"enabled" : L"disabled"));
93 
94     return Status;
95 }
96 
97 VOID
98 TestUnload(
99     _In_ PDRIVER_OBJECT DriverObject)
100 {
101     PAGED_CODE();
102 }
103 
104 BOOLEAN
105 NTAPI
106 AcquireForLazyWrite(
107     _In_ PVOID Context,
108     _In_ BOOLEAN Wait)
109 {
110     return TRUE;
111 }
112 
113 VOID
114 NTAPI
115 ReleaseFromLazyWrite(
116     _In_ PVOID Context)
117 {
118     return;
119 }
120 
121 BOOLEAN
122 NTAPI
123 AcquireForReadAhead(
124     _In_ PVOID Context,
125     _In_ BOOLEAN Wait)
126 {
127     return TRUE;
128 }
129 
130 VOID
131 NTAPI
132 ReleaseFromReadAhead(
133     _In_ PVOID Context)
134 {
135     return;
136 }
137 
138 static CACHE_MANAGER_CALLBACKS Callbacks = {
139     AcquireForLazyWrite,
140     ReleaseFromLazyWrite,
141     AcquireForReadAhead,
142     ReleaseFromReadAhead,
143 };
144 
145 static CC_FILE_SIZES FileSizes = {
146     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .AllocationSize
147     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000), // .FileSize
148     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0x4000)  // .ValidDataLength
149 };
150 
151 static CC_FILE_SIZES SmallFileSizes = {
152     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)512), // .AllocationSize
153     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)496), // .FileSize
154     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)496)  // .ValidDataLength
155 };
156 
157 static
158 PVOID
159 MapAndLockUserBuffer(
160     _In_ _Out_ PIRP Irp,
161     _In_ ULONG BufferLength)
162 {
163     PMDL Mdl;
164 
165     if (Irp->MdlAddress == NULL)
166     {
167         Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
168         if (Mdl == NULL)
169         {
170             return NULL;
171         }
172 
173         _SEH2_TRY
174         {
175             MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess);
176         }
177         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
178         {
179             IoFreeMdl(Mdl);
180             Irp->MdlAddress = NULL;
181             _SEH2_YIELD(return NULL);
182         }
183         _SEH2_END;
184     }
185 
186     return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
187 }
188 
189 #define ok_bcb(B, L, O)                                                                   \
190 {                                                                                         \
191     PPUBLIC_BCB public_bcb = (B);                                                         \
192     ok(public_bcb->NodeTypeCode == 0x2FD, "Not a BCB: %04x\n", public_bcb->NodeTypeCode); \
193     ok(public_bcb->NodeByteSize == 0, "Invalid size: %d\n", public_bcb->NodeByteSize);    \
194     ok_eq_ulong(public_bcb->MappedLength, (L));                                           \
195     ok_eq_longlong(public_bcb->MappedFileOffset.QuadPart, (O));                           \
196 }
197 
198 static
199 VOID
200 NTAPI
201 PinInAnotherThread(IN PVOID Context)
202 {
203     BOOLEAN Ret;
204     PULONG Buffer;
205     PVOID Bcb;
206     LARGE_INTEGER Offset;
207     PTEST_CONTEXT TestContext;
208 
209     ok(TestFileObject != NULL, "Called in invalid context!\n");
210     ok_eq_ulong(TestTestId, 3);
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 = 0x1000;
220     KmtStartSeh();
221     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
222     KmtEndSeh(STATUS_SUCCESS);
223 
224     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
225     {
226         ok_bcb(Bcb, 12288, Offset.QuadPart);
227         ok_eq_pointer(Bcb, TestContext->Bcb);
228         ok_eq_pointer(Buffer, TestContext->Buffer);
229 
230         CcUnpinData(Bcb);
231     }
232 
233     KmtStartSeh();
234     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_WAIT | PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
235     KmtEndSeh(STATUS_SUCCESS);
236 
237     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
238     {
239         ok_bcb(Bcb, 12288, Offset.QuadPart);
240         ok_eq_pointer(Bcb, TestContext->Bcb);
241         ok_eq_pointer(Buffer, TestContext->Buffer);
242 
243         CcUnpinData(Bcb);
244     }
245 
246     KmtStartSeh();
247     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer);
248     KmtEndSeh(STATUS_SUCCESS);
249 
250     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
251     {
252         ok_bcb(Bcb, 12288, Offset.QuadPart);
253         ok_eq_pointer(Bcb, TestContext->Bcb);
254         ok_eq_pointer(Buffer, TestContext->Buffer);
255 
256         CcUnpinData(Bcb);
257     }
258 
259     KmtStartSeh();
260     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
261     KmtEndSeh(STATUS_SUCCESS);
262 
263     if (!skip(Ret == TRUE, "CcMapData failed\n"))
264     {
265         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
266         ok_eq_pointer(Buffer, TestContext->Buffer);
267 
268         CcUnpinData(Bcb);
269     }
270 
271     Offset.QuadPart = 0x1500;
272     TestContext->Length -= 0x500;
273 
274     KmtStartSeh();
275     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_WAIT | PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
276     KmtEndSeh(STATUS_SUCCESS);
277 
278     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
279     {
280         ok_bcb(Bcb, 12288, 4096);
281         ok_eq_pointer(Bcb, TestContext->Bcb);
282         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
283 
284         CcUnpinData(Bcb);
285     }
286 
287     KmtStartSeh();
288     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
289     KmtEndSeh(STATUS_SUCCESS);
290 
291     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
292     {
293         ok_bcb(Bcb, 12288, 4096);
294         ok_eq_pointer(Bcb, TestContext->Bcb);
295         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
296 
297         CcUnpinData(Bcb);
298     }
299 
300     KmtStartSeh();
301     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer);
302     KmtEndSeh(STATUS_SUCCESS);
303 
304     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
305     {
306         ok_bcb(Bcb, 12288, 4096);
307         ok_eq_pointer(Bcb, TestContext->Bcb);
308         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
309 
310         CcUnpinData(Bcb);
311     }
312 
313     return;
314 }
315 
316 static
317 VOID
318 NTAPI
319 PinInAnotherThreadExclusive(IN PVOID Context)
320 {
321     BOOLEAN Ret;
322     PULONG Buffer;
323     PVOID Bcb;
324     LARGE_INTEGER Offset;
325     PTEST_CONTEXT TestContext;
326 
327     ok(TestFileObject != NULL, "Called in invalid context!\n");
328     ok_eq_ulong(TestTestId, 3);
329 
330     TestContext = Context;
331     ok(TestContext != NULL, "Called in invalid context!\n");
332     ok(TestContext->Bcb != NULL, "Called in invalid context!\n");
333     ok(TestContext->Buffer != NULL, "Called in invalid context!\n");
334     ok(TestContext->Length != 0, "Called in invalid context!\n");
335 
336     Ret = FALSE;
337     Offset.QuadPart = 0x1000;
338     KmtStartSeh();
339     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer);
340     KmtEndSeh(STATUS_SUCCESS);
341     ok(Ret == FALSE, "CcPinRead succeed\n");
342 
343     if (Ret)
344     {
345         CcUnpinData(Bcb);
346     }
347 
348     KmtStartSeh();
349     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer);
350     KmtEndSeh(STATUS_SUCCESS);
351 
352     if (!skip(Ret == TRUE, "CcMapData failed\n"))
353     {
354         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
355         ok_eq_pointer(Buffer, TestContext->Buffer);
356 
357         CcUnpinData(Bcb);
358     }
359 
360     Offset.QuadPart = 0x1500;
361     TestContext->Length -= 0x500;
362 
363     KmtStartSeh();
364     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
365     KmtEndSeh(STATUS_SUCCESS);
366     ok(Ret == FALSE, "CcPinRead succeed\n");
367 
368     if (Ret)
369     {
370         CcUnpinData(Bcb);
371     }
372 
373     KmtStartSeh();
374     Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer);
375     KmtEndSeh(STATUS_SUCCESS);
376     ok(Ret == FALSE, "CcPinRead succeed\n");
377 
378     if (Ret)
379     {
380         CcUnpinData(Bcb);
381     }
382 
383     KmtStartSeh();
384     Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer);
385     KmtEndSeh(STATUS_SUCCESS);
386 
387     if (!skip(Ret == TRUE, "CcMapData failed\n"))
388     {
389         ok(Bcb != TestContext->Bcb, "Returned same BCB!\n");
390         ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500));
391 
392         CcUnpinData(Bcb);
393     }
394 
395     return;
396 }
397 
398 static
399 VOID
400 PerformTest(
401     ULONG TestId,
402     PDEVICE_OBJECT DeviceObject)
403 {
404     PVOID Bcb;
405     BOOLEAN Ret;
406     PULONG Buffer;
407     PTEST_FCB Fcb;
408     LARGE_INTEGER Offset;
409 
410     ok_eq_pointer(TestFileObject, NULL);
411     ok_eq_pointer(TestDeviceObject, NULL);
412     ok_eq_ulong(TestTestId, -1);
413 
414     TestDeviceObject = DeviceObject;
415     TestTestId = TestId;
416     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
417     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
418     {
419         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
420         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
421         {
422             BOOLEAN PinAccess = (TestId != 4);
423 
424             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
425             ExInitializeFastMutex(&Fcb->HeaderMutex);
426             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
427 
428             TestFileObject->FsContext = Fcb;
429             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
430 
431             KmtStartSeh();
432             if (TestId < 6)
433             {
434                 CcInitializeCacheMap(TestFileObject, &FileSizes, PinAccess, &Callbacks, NULL);
435             }
436             else
437             {
438                 CcInitializeCacheMap(TestFileObject, &SmallFileSizes, PinAccess, &Callbacks, NULL);
439             }
440             KmtEndSeh(STATUS_SUCCESS);
441 
442             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
443             {
444                 if (TestId < 3)
445                 {
446                     Ret = FALSE;
447                     Offset.QuadPart = TestId * 0x1000;
448                     KmtStartSeh();
449                     Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
450                     KmtEndSeh(STATUS_SUCCESS);
451 
452                     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
453                     {
454                         ok_bcb(Bcb, ((4 - TestId) * 4096), Offset.QuadPart);
455                         ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE);
456 
457                         CcUnpinData(Bcb);
458                     }
459                 }
460                 else if (TestId == 3)
461                 {
462                     PTEST_CONTEXT TestContext;
463 
464                     TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT));
465                     if (!skip(TestContext != NULL, "ExAllocatePool failed\n"))
466                     {
467                         Ret = FALSE;
468                         Offset.QuadPart = 0x1000;
469 
470                         /* Try enforce BCB first */
471                         KmtStartSeh();
472                         Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT | PIN_IF_BCB, &Bcb, (PVOID *)&Buffer);
473                         KmtEndSeh(STATUS_SUCCESS);
474                         ok(Ret == FALSE, "CcPinRead succeed\n");
475                         if (Ret)
476                         {
477                             CcUnpinData(Bcb);
478                         }
479 
480                         KmtStartSeh();
481                         Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &TestContext->Bcb, &TestContext->Buffer);
482                         KmtEndSeh(STATUS_SUCCESS);
483 
484                         if (!skip(Ret == TRUE, "CcPinRead failed\n"))
485                         {
486                             PKTHREAD ThreadHandle;
487 
488                             ok_bcb(TestContext->Bcb, 12288, Offset.QuadPart);
489 
490                             /* That's a bit rough but should do the job */
491 #ifdef _X86_
492                             if (Memory >= 2 * 1024)
493                             {
494                                 ok((TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xE0FFFFFF) ||
495                                    (TestContext->Buffer >= (PVOID)0xA4000000 && TestContext->Buffer < (PVOID)0xBFFFFFFF),
496                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
497                             }
498                             else if (TS)
499                             {
500                                 ok(TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF,
501                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
502                             }
503                             else
504                             {
505                                 ok(TestContext->Buffer >= (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDBFFFFFF,
506                                    "Buffer %p not mapped in system space\n", TestContext->Buffer);
507                             }
508 #elif defined(_M_AMD64)
509                             ok(TestContext->Buffer >= (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000,
510                                "Buffer %p not mapped in system space\n", TestContext->Buffer);
511 #else
512                             skip(FALSE, "System space mapping not defined\n");
513 #endif
514 
515                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
516                             ThreadHandle = KmtStartThread(PinInAnotherThread, TestContext);
517                             KmtFinishThread(ThreadHandle, NULL);
518 
519                             TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart;
520                             ThreadHandle = KmtStartThread(PinInAnotherThread, TestContext);
521                             KmtFinishThread(ThreadHandle, NULL);
522 
523                             CcUnpinData(TestContext->Bcb);
524                         }
525 
526                         KmtStartSeh();
527                         Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT | PIN_EXCLUSIVE, &TestContext->Bcb, &TestContext->Buffer);
528                         KmtEndSeh(STATUS_SUCCESS);
529 
530                         if (!skip(Ret == TRUE, "CcPinRead failed\n"))
531                         {
532                             PKTHREAD ThreadHandle;
533 
534                             ok_bcb(TestContext->Bcb, 12288, Offset.QuadPart);
535 
536                             TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart;
537                             ThreadHandle = KmtStartThread(PinInAnotherThreadExclusive, TestContext);
538                             KmtFinishThread(ThreadHandle, NULL);
539 
540                             CcUnpinData(TestContext->Bcb);
541                         }
542 
543                         ExFreePool(TestContext);
544                     }
545                 }
546                 else if (TestId == 4)
547                 {
548                     Ret = FALSE;
549                     Offset.QuadPart = 0x1000;
550                     KmtStartSeh();
551                     Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
552                     KmtEndSeh(STATUS_SUCCESS);
553 
554                     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
555                     {
556                         ok_bcb(Bcb, 12288, Offset.QuadPart);
557                         ok_eq_ulong(Buffer[0x2000 / sizeof(ULONG)], 0);
558 
559                         CcUnpinData(Bcb);
560                     }
561                 }
562                 else if (TestId == 5)
563                 {
564                     FileSizes.AllocationSize.QuadPart += VACB_MAPPING_GRANULARITY;
565                     CcSetFileSizes(TestFileObject, &FileSizes);
566 
567                     /* Pin after EOF */
568                     Ret = FALSE;
569                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000;
570 
571                     KmtStartSeh();
572                     Ret = CcPinRead(TestFileObject, &Offset, 0x1000, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
573                     KmtEndSeh(STATUS_SUCCESS);
574                     ok(Ret == TRUE, "CcPinRead failed\n");
575                     if (Ret)
576                     {
577                         CcUnpinData(Bcb);
578                     }
579 
580                     /* Pin a VACB after EOF */
581                     Ret = FALSE;
582                     Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY;
583 
584                     KmtStartSeh();
585                     Ret = CcPinRead(TestFileObject, &Offset, 0x1000, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
586                     KmtEndSeh(STATUS_SUCCESS);
587                     ok(Ret == TRUE, "CcPinRead failed\n");
588                     if (Ret)
589                     {
590                         CcUnpinData(Bcb);
591                     }
592 
593                     /* Pin after Allocation */
594                     Ret = FALSE;
595                     Offset.QuadPart = FileSizes.AllocationSize.QuadPart + 0x1000;
596 
597                     KmtStartSeh();
598                     Ret = CcPinRead(TestFileObject, &Offset, 0x1000, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
599                     KmtEndSeh(STATUS_SUCCESS);
600                     ok(Ret == TRUE, "CcPinRead failed\n");
601                     if (Ret)
602                     {
603                         /* Set this one dirty */
604                         CcSetDirtyPinnedData(Bcb, NULL);
605 
606                         CcUnpinData(Bcb);
607 
608                         WriteOffset.QuadPart = -1LL;
609                         WriteLength = MAXULONG;
610                         /* Flush to trigger the write */
611                         CcFlushCache(TestFileObject->SectionObjectPointer, &Offset, 0x1000, NULL);
612 
613                         ok_eq_longlong(WriteOffset.QuadPart, Offset.QuadPart);
614                         ok_eq_ulong(WriteLength, 0x1000);
615                     }
616 
617                     /* Pin a VACB after Allocation */
618                     Ret = FALSE;
619                     Offset.QuadPart = FileSizes.AllocationSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY;
620 
621                     KmtStartSeh();
622                     Ret = CcPinRead(TestFileObject, &Offset, 0x1000, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
623                     KmtEndSeh(STATUS_SUCCESS);
624                     ok(Ret == TRUE, "CcPinRead failed\n");
625                     if (Ret)
626                     {
627                         /* Set this one dirty */
628                         CcSetDirtyPinnedData(Bcb, NULL);
629 
630                         CcUnpinData(Bcb);
631 
632                         WriteOffset.QuadPart = -1LL;
633                         WriteLength = MAXULONG;
634                         /* Flush to trigger the write */
635                         CcFlushCache(TestFileObject->SectionObjectPointer, &Offset, 0x1000, NULL);
636 
637                         ok_eq_longlong(WriteOffset.QuadPart, Offset.QuadPart);
638                         ok_eq_ulong(WriteLength, 0x1000);
639                     }
640 
641                     /* Pin more than a VACB */
642                     Ret = FALSE;
643                     Offset.QuadPart = 0x0;
644 
645                     KmtStartSeh();
646                     Ret = CcPinRead(TestFileObject, &Offset, 0x1000 + VACB_MAPPING_GRANULARITY, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
647                     KmtEndSeh(STATUS_SUCCESS);
648                     ok(Ret == TRUE, "CcPinRead failed\n");
649                     if (Ret)
650                     {
651                         /* Set this one dirty */
652                         CcSetDirtyPinnedData(Bcb, NULL);
653 
654                         CcUnpinData(Bcb);
655 
656                         /* The data is dirtified through the VACB */
657                         WriteOffset.QuadPart = -1LL;
658                         WriteLength = MAXULONG;
659                         CcFlushCache(TestFileObject->SectionObjectPointer, &Offset, VACB_MAPPING_GRANULARITY + 0x1000, NULL);
660 
661                         ok_eq_longlong(WriteOffset.QuadPart, Offset.QuadPart);
662                         ok_eq_ulong(WriteLength, VACB_MAPPING_GRANULARITY + 0x1000);
663                     }
664                 }
665                 else if (TestId == 6)
666                 {
667                     Ret = FALSE;
668                     Offset.QuadPart = 0;
669 
670                     KmtStartSeh();
671                     Ret = CcPinRead(TestFileObject, &Offset, FileSizes.FileSize.QuadPart, PIN_WAIT, &Bcb, (PVOID *)&Buffer);
672                     KmtEndSeh(STATUS_SUCCESS);
673 
674                     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
675                     {
676                         ok_bcb(Bcb, PAGE_SIZE * 4, Offset.QuadPart);
677                         RtlFillMemory(Buffer, 0xbd, FileSizes.FileSize.LowPart);
678                         CcSetDirtyPinnedData(Bcb, NULL);
679 
680                         CcUnpinData(Bcb);
681                     }
682                 }
683             }
684         }
685     }
686 }
687 
688 
689 static
690 VOID
691 CleanupTest(
692     ULONG TestId,
693     PDEVICE_OBJECT DeviceObject)
694 {
695     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
696     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
697 
698     ok_eq_pointer(TestDeviceObject, DeviceObject);
699     ok_eq_ulong(TestTestId, TestId);
700 
701     if (!skip(TestFileObject != NULL, "No test FO\n"))
702     {
703         if (CcIsFileCached(TestFileObject))
704         {
705             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
706             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
707             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
708         }
709 
710         if (TestFileObject->FsContext != NULL)
711         {
712             ExFreePool(TestFileObject->FsContext);
713             TestFileObject->FsContext = NULL;
714             TestFileObject->SectionObjectPointer = NULL;
715         }
716 
717         if (TestTestId == 6)
718         {
719             ok_bool_true(TestWriteCalled, "Write was not called!\n");
720         }
721         else
722         {
723             ok_bool_false(TestWriteCalled, "Write was unexpectedly called\n");
724         }
725 
726         ObDereferenceObject(TestFileObject);
727     }
728 
729     TestFileObject = NULL;
730     TestDeviceObject = NULL;
731     TestTestId = -1;
732     TestWriteCalled = FALSE;
733 }
734 
735 
736 static
737 NTSTATUS
738 TestMessageHandler(
739     _In_ PDEVICE_OBJECT DeviceObject,
740     _In_ ULONG ControlCode,
741     _In_opt_ PVOID Buffer,
742     _In_ SIZE_T InLength,
743     _Inout_ PSIZE_T OutLength)
744 {
745     NTSTATUS Status = STATUS_SUCCESS;
746 
747     FsRtlEnterFileSystem();
748 
749     switch (ControlCode)
750     {
751         case IOCTL_START_TEST:
752             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
753             PerformTest(*(PULONG)Buffer, DeviceObject);
754             break;
755 
756         case IOCTL_FINISH_TEST:
757             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
758             CleanupTest(*(PULONG)Buffer, DeviceObject);
759             break;
760 
761         default:
762             Status = STATUS_NOT_IMPLEMENTED;
763             break;
764     }
765 
766     FsRtlExitFileSystem();
767 
768     return Status;
769 }
770 
771 static
772 NTSTATUS
773 TestIrpHandler(
774     _In_ PDEVICE_OBJECT DeviceObject,
775     _In_ PIRP Irp,
776     _In_ PIO_STACK_LOCATION IoStack)
777 {
778     NTSTATUS Status;
779 
780     PAGED_CODE();
781 
782     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
783     ASSERT(IoStack->MajorFunction == IRP_MJ_READ ||
784            IoStack->MajorFunction == IRP_MJ_WRITE);
785 
786     FsRtlEnterFileSystem();
787 
788     Status = STATUS_NOT_SUPPORTED;
789     Irp->IoStatus.Information = 0;
790 
791     if (IoStack->MajorFunction == IRP_MJ_READ)
792     {
793         PMDL Mdl;
794         ULONG Length;
795         PVOID Buffer;
796         LARGE_INTEGER Offset;
797 
798         Offset = IoStack->Parameters.Read.ByteOffset;
799         Length = IoStack->Parameters.Read.Length;
800 
801         ok_eq_pointer(DeviceObject, TestDeviceObject);
802         ok_eq_pointer(IoStack->FileObject, TestFileObject);
803 
804         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
805 
806         ok_irql(APC_LEVEL);
807         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
808         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
809 
810         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
811         Buffer = MapAndLockUserBuffer(Irp, Length);
812         ok(Buffer != NULL, "Null pointer!\n");
813         RtlFillMemory(Buffer, Length, 0xBA);
814 
815         Status = STATUS_SUCCESS;
816         if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000)
817         {
818             *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE;
819         }
820 
821         Mdl = Irp->MdlAddress;
822         ok(Mdl != NULL, "Null pointer for MDL!\n");
823         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
824         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
825         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
826         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
827 
828         Irp->IoStatus.Information = Length;
829     }
830     else if (IoStack->MajorFunction == IRP_MJ_WRITE)
831     {
832         PMDL Mdl;
833         ULONG Length;
834         PVOID Buffer;
835         LARGE_INTEGER Offset;
836 
837         Offset = IoStack->Parameters.Write.ByteOffset;
838         Length = IoStack->Parameters.Write.Length;
839 
840         ok((TestTestId == 5) || (TestTestId == 6), "Unexpected test id: %d\n", TestTestId);
841         ok_eq_pointer(DeviceObject, TestDeviceObject);
842         ok_eq_pointer(IoStack->FileObject, TestFileObject);
843 
844         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
845 
846         ok_irql(PASSIVE_LEVEL);
847 
848         if (TestTestId == 5)
849         {
850             /* This assumes continuous writes. */
851             if (WriteOffset.QuadPart != -1)
852             {
853                 LONGLONG WriteEnd = WriteOffset.QuadPart + WriteLength;
854 
855                 if (WriteOffset.QuadPart > Offset.QuadPart)
856                     WriteOffset = Offset;
857                 if (WriteEnd < (Offset.QuadPart + Length))
858                     WriteLength = (Offset.QuadPart + Length) - WriteOffset.QuadPart;
859             }
860             else
861             {
862                 WriteOffset = Offset;
863                 WriteLength = Length;
864             }
865         }
866         else
867         {
868             ok(Offset.QuadPart == 0, "Offset is not null: %I64i\n", Offset.QuadPart);
869             ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
870             ok(Length == PAGE_SIZE * 4, "Length is not MappedLength-sized: %I64i\n", Length);
871 
872             Buffer = MapAndLockUserBuffer(Irp, Length);
873             ok(Buffer != NULL, "Null pointer!\n");
874 
875             Mdl = Irp->MdlAddress;
876             ok(Mdl != NULL, "Null pointer for MDL!\n");
877             ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
878             ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
879             ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
880 
881             ok_bool_false(TestWriteCalled, "Write has been unexpectedly called twice!\n");
882             TestWriteCalled = TRUE;
883         }
884 
885         Status = STATUS_SUCCESS;
886         Irp->IoStatus.Information = Length;
887     }
888 
889     if (Status == STATUS_PENDING)
890     {
891         IoMarkIrpPending(Irp);
892         IoCompleteRequest(Irp, IO_NO_INCREMENT);
893         Status = STATUS_PENDING;
894     }
895     else
896     {
897         Irp->IoStatus.Status = Status;
898         IoCompleteRequest(Irp, IO_NO_INCREMENT);
899     }
900 
901     FsRtlExitFileSystem();
902 
903     return Status;
904 }
905