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