1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for CcCopyRead 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 typedef struct _TEST_FCB
14 {
15     FSRTL_ADVANCED_FCB_HEADER Header;
16     SECTION_OBJECT_POINTERS SectionObjectPointers;
17     FAST_MUTEX HeaderMutex;
18 } TEST_FCB, *PTEST_FCB;
19 
20 static PFILE_OBJECT TestFileObject;
21 static PDEVICE_OBJECT TestDeviceObject;
22 static KMT_IRP_HANDLER TestIrpHandler;
23 static FAST_IO_DISPATCH TestFastIoDispatch;
24 
25 static
26 BOOLEAN
27 NTAPI
28 FastIoRead(
29     _In_ PFILE_OBJECT FileObject,
30     _In_ PLARGE_INTEGER FileOffset,
31     _In_ ULONG Length,
32     _In_ BOOLEAN Wait,
33     _In_ ULONG LockKey,
34     _Out_ PVOID Buffer,
35     _Out_ PIO_STATUS_BLOCK IoStatus,
36     _In_ PDEVICE_OBJECT DeviceObject)
37 {
38     IoStatus->Status = STATUS_NOT_SUPPORTED;
39     return FALSE;
40 }
41 
42 NTSTATUS
43 TestEntry(
44     _In_ PDRIVER_OBJECT DriverObject,
45     _In_ PCUNICODE_STRING RegistryPath,
46     _Out_ PCWSTR *DeviceName,
47     _Inout_ INT *Flags)
48 {
49     NTSTATUS Status = STATUS_SUCCESS;
50 
51     PAGED_CODE();
52 
53     UNREFERENCED_PARAMETER(RegistryPath);
54 
55     *DeviceName = L"CcCopyRead";
56     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
57              TESTENTRY_BUFFERED_IO_DEVICE |
58              TESTENTRY_NO_READONLY_DEVICE;
59 
60     KmtRegisterIrpHandler(IRP_MJ_CLEANUP, NULL, TestIrpHandler);
61     KmtRegisterIrpHandler(IRP_MJ_CREATE, NULL, TestIrpHandler);
62     KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
63 
64     TestFastIoDispatch.FastIoRead = FastIoRead;
65     DriverObject->FastIoDispatch = &TestFastIoDispatch;
66 
67 
68     return Status;
69 }
70 
71 VOID
72 TestUnload(
73     _In_ PDRIVER_OBJECT DriverObject)
74 {
75     PAGED_CODE();
76 }
77 
78 BOOLEAN
79 NTAPI
80 AcquireForLazyWrite(
81     _In_ PVOID Context,
82     _In_ BOOLEAN Wait)
83 {
84     return TRUE;
85 }
86 
87 VOID
88 NTAPI
89 ReleaseFromLazyWrite(
90     _In_ PVOID Context)
91 {
92     return;
93 }
94 
95 BOOLEAN
96 NTAPI
97 AcquireForReadAhead(
98     _In_ PVOID Context,
99     _In_ BOOLEAN Wait)
100 {
101     return TRUE;
102 }
103 
104 VOID
105 NTAPI
106 ReleaseFromReadAhead(
107     _In_ PVOID Context)
108 {
109     return;
110 }
111 
112 static CACHE_MANAGER_CALLBACKS Callbacks = {
113     AcquireForLazyWrite,
114     ReleaseFromLazyWrite,
115     AcquireForReadAhead,
116     ReleaseFromReadAhead,
117 };
118 
119 static
120 PVOID
121 MapAndLockUserBuffer(
122     _In_ _Out_ PIRP Irp,
123     _In_ ULONG BufferLength)
124 {
125     PMDL Mdl;
126 
127     if (Irp->MdlAddress == NULL)
128     {
129         Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
130         if (Mdl == NULL)
131         {
132             return NULL;
133         }
134 
135         _SEH2_TRY
136         {
137             MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess);
138         }
139         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
140         {
141             IoFreeMdl(Mdl);
142             Irp->MdlAddress = NULL;
143             _SEH2_YIELD(return NULL);
144         }
145         _SEH2_END;
146     }
147 
148     return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
149 }
150 
151 
152 static
153 NTSTATUS
154 TestIrpHandler(
155     _In_ PDEVICE_OBJECT DeviceObject,
156     _In_ PIRP Irp,
157     _In_ PIO_STACK_LOCATION IoStack)
158 {
159     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
160     NTSTATUS Status;
161     PTEST_FCB Fcb;
162     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
163 
164     PAGED_CODE();
165 
166     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
167     ASSERT(IoStack->MajorFunction == IRP_MJ_CLEANUP ||
168            IoStack->MajorFunction == IRP_MJ_CREATE ||
169            IoStack->MajorFunction == IRP_MJ_READ);
170 
171     Status = STATUS_NOT_SUPPORTED;
172     Irp->IoStatus.Information = 0;
173 
174     if (IoStack->MajorFunction == IRP_MJ_CREATE)
175     {
176         ok_irql(PASSIVE_LEVEL);
177 
178         if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR))
179         {
180             TestDeviceObject = DeviceObject;
181             TestFileObject = IoStack->FileObject;
182         }
183         Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Fcb), 'FwrI');
184         RtlZeroMemory(Fcb, sizeof(*Fcb));
185         ExInitializeFastMutex(&Fcb->HeaderMutex);
186         FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
187         if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
188             IoStack->FileObject->FileName.Buffer[1] == 'B')
189         {
190             Fcb->Header.AllocationSize.QuadPart = 1000000;
191             Fcb->Header.FileSize.QuadPart = 1000000;
192             Fcb->Header.ValidDataLength.QuadPart = 1000000;
193         }
194         else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
195                  IoStack->FileObject->FileName.Buffer[1] == 'S')
196         {
197             Fcb->Header.AllocationSize.QuadPart = 1004;
198             Fcb->Header.FileSize.QuadPart = 1004;
199             Fcb->Header.ValidDataLength.QuadPart = 1004;
200         }
201         else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) &&
202                  IoStack->FileObject->FileName.Buffer[1] == 'R')
203         {
204             Fcb->Header.AllocationSize.QuadPart = 62;
205             Fcb->Header.FileSize.QuadPart = 62;
206             Fcb->Header.ValidDataLength.QuadPart = 62;
207         }
208         else
209         {
210             Fcb->Header.AllocationSize.QuadPart = 512;
211             Fcb->Header.FileSize.QuadPart = 512;
212             Fcb->Header.ValidDataLength.QuadPart = 512;
213         }
214         Fcb->Header.IsFastIoPossible = FastIoIsNotPossible;
215         IoStack->FileObject->FsContext = Fcb;
216         IoStack->FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
217 
218         CcInitializeCacheMap(IoStack->FileObject,
219                              (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
220                              FALSE, &Callbacks, NULL);
221 
222         Irp->IoStatus.Information = FILE_OPENED;
223         Status = STATUS_SUCCESS;
224     }
225     else if (IoStack->MajorFunction == IRP_MJ_READ)
226     {
227         BOOLEAN Ret;
228         ULONG Length;
229         PVOID Buffer;
230         LARGE_INTEGER Offset;
231 
232         Offset = IoStack->Parameters.Read.ByteOffset;
233         Length = IoStack->Parameters.Read.Length;
234         Fcb = IoStack->FileObject->FsContext;
235 
236         ok_eq_pointer(DeviceObject, TestDeviceObject);
237         ok_eq_pointer(IoStack->FileObject, TestFileObject);
238 
239         if (!FlagOn(Irp->Flags, IRP_NOCACHE))
240         {
241             ok_irql(PASSIVE_LEVEL);
242             ok(Offset.QuadPart % PAGE_SIZE != 0, "Offset is aligned: %I64i\n", Offset.QuadPart);
243             ok(Length % PAGE_SIZE != 0, "Length is aligned: %I64i\n", Length);
244 
245             Buffer = Irp->AssociatedIrp.SystemBuffer;
246             ok(Buffer != NULL, "Null pointer!\n");
247 
248             _SEH2_TRY
249             {
250                 Ret = CcCopyRead(IoStack->FileObject, &Offset, Length, TRUE, Buffer,
251                                  &Irp->IoStatus);
252                 ok_bool_true(Ret, "CcCopyRead");
253             }
254             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
255             {
256                 Irp->IoStatus.Status = _SEH2_GetExceptionCode();
257             }
258             _SEH2_END;
259 
260             Status = Irp->IoStatus.Status;
261 
262             if (NT_SUCCESS(Status))
263             {
264                 if (Offset.QuadPart <= 1000LL && Offset.QuadPart + Length > 1000LL)
265                 {
266                     ok_eq_hex(*(PUSHORT)((ULONG_PTR)Buffer + (ULONG_PTR)(1000LL - Offset.QuadPart)), 0xFFFF);
267                 }
268                 else
269                 {
270                     ok_eq_hex(*(PUSHORT)Buffer, 0xBABA);
271                 }
272             }
273         }
274         else
275         {
276             PMDL Mdl;
277 
278             ok_irql(APC_LEVEL);
279             ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
280             ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
281 
282             ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
283             Buffer = MapAndLockUserBuffer(Irp, Length);
284             ok(Buffer != NULL, "Null pointer!\n");
285             RtlFillMemory(Buffer, Length, 0xBA);
286 
287             Status = STATUS_SUCCESS;
288             if (Offset.QuadPart <= 1000LL && Offset.QuadPart + Length > 1000LL)
289             {
290                 *(PUSHORT)((ULONG_PTR)Buffer + (ULONG_PTR)(1000LL - Offset.QuadPart)) = 0xFFFF;
291             }
292 
293             Mdl = Irp->MdlAddress;
294             ok(Mdl != NULL, "Null pointer for MDL!\n");
295             ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
296             ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
297             ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
298             ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
299         }
300 
301         if (NT_SUCCESS(Status))
302         {
303             Irp->IoStatus.Information = Length;
304             IoStack->FileObject->CurrentByteOffset.QuadPart = Offset.QuadPart + Length;
305         }
306     }
307     else if (IoStack->MajorFunction == IRP_MJ_CLEANUP)
308     {
309         ok_irql(PASSIVE_LEVEL);
310         KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
311         CcUninitializeCacheMap(IoStack->FileObject, &Zero, &CacheUninitEvent);
312         KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
313         Fcb = IoStack->FileObject->FsContext;
314         ExFreePoolWithTag(Fcb, 'FwrI');
315         IoStack->FileObject->FsContext = NULL;
316         Status = STATUS_SUCCESS;
317     }
318 
319     if (Status == STATUS_PENDING)
320     {
321         IoMarkIrpPending(Irp);
322         IoCompleteRequest(Irp, IO_NO_INCREMENT);
323         Status = STATUS_PENDING;
324     }
325     else
326     {
327         Irp->IoStatus.Status = Status;
328         IoCompleteRequest(Irp, IO_NO_INCREMENT);
329     }
330 
331     return Status;
332 }
333