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