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