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