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