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