1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test driver for CcSetFileSizes 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 static ULONG TestTestId = -1;
24 static PFILE_OBJECT TestFileObject;
25 static PDEVICE_OBJECT TestDeviceObject;
26 static KMT_IRP_HANDLER TestIrpHandler;
27 static KMT_MESSAGE_HANDLER TestMessageHandler;
28 static BOOLEAN TestUnpin = FALSE;
29 static BOOLEAN TestSizing = FALSE;
30 static BOOLEAN TestDirtying = FALSE;
31 static BOOLEAN TestUncaching = FALSE;
32 static BOOLEAN TestWritten = FALSE;
33 
34 NTSTATUS
35 TestEntry(
36     _In_ PDRIVER_OBJECT DriverObject,
37     _In_ PCUNICODE_STRING RegistryPath,
38     _Out_ PCWSTR *DeviceName,
39     _Inout_ INT *Flags)
40 {
41     NTSTATUS Status = STATUS_SUCCESS;
42 
43     PAGED_CODE();
44 
45     UNREFERENCED_PARAMETER(RegistryPath);
46 
47     *DeviceName = L"CcSetFileSizes";
48     *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE |
49              TESTENTRY_BUFFERED_IO_DEVICE |
50              TESTENTRY_NO_READONLY_DEVICE;
51 
52     KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler);
53     KmtRegisterIrpHandler(IRP_MJ_WRITE, NULL, TestIrpHandler);
54     KmtRegisterMessageHandler(0, NULL, TestMessageHandler);
55 
56     return Status;
57 }
58 
59 VOID
60 TestUnload(
61     _In_ PDRIVER_OBJECT DriverObject)
62 {
63     PAGED_CODE();
64 }
65 
66 BOOLEAN
67 NTAPI
68 AcquireForLazyWrite(
69     _In_ PVOID Context,
70     _In_ BOOLEAN Wait)
71 {
72     return TRUE;
73 }
74 
75 VOID
76 NTAPI
77 ReleaseFromLazyWrite(
78     _In_ PVOID Context)
79 {
80     return;
81 }
82 
83 BOOLEAN
84 NTAPI
85 AcquireForReadAhead(
86     _In_ PVOID Context,
87     _In_ BOOLEAN Wait)
88 {
89     return TRUE;
90 }
91 
92 VOID
93 NTAPI
94 ReleaseFromReadAhead(
95     _In_ PVOID Context)
96 {
97     return;
98 }
99 
100 static CACHE_MANAGER_CALLBACKS Callbacks = {
101     AcquireForLazyWrite,
102     ReleaseFromLazyWrite,
103     AcquireForReadAhead,
104     ReleaseFromReadAhead,
105 };
106 
107 static CC_FILE_SIZES NewFileSizes = {
108     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)VACB_MAPPING_GRANULARITY), // .AllocationSize
109     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)VACB_MAPPING_GRANULARITY), // .FileSize
110     RTL_CONSTANT_LARGE_INTEGER((LONGLONG)VACB_MAPPING_GRANULARITY)  // .ValidDataLength
111 };
112 
113 static
114 PVOID
115 MapAndLockUserBuffer(
116     _In_ _Out_ PIRP Irp,
117     _In_ ULONG BufferLength)
118 {
119     PMDL Mdl;
120 
121     if (Irp->MdlAddress == NULL)
122     {
123         Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp);
124         if (Mdl == NULL)
125         {
126             return NULL;
127         }
128 
129         _SEH2_TRY
130         {
131             MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess);
132         }
133         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
134         {
135             IoFreeMdl(Mdl);
136             Irp->MdlAddress = NULL;
137             _SEH2_YIELD(return NULL);
138         }
139         _SEH2_END;
140     }
141 
142     return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
143 }
144 
145 static
146 VOID
147 PerformTest(
148     ULONG TestId,
149     PDEVICE_OBJECT DeviceObject)
150 {
151     PVOID Bcb;
152     BOOLEAN Ret;
153     PULONG Buffer;
154     PTEST_FCB Fcb;
155     LARGE_INTEGER Offset;
156     IO_STATUS_BLOCK IoStatus;
157 
158     ok_eq_pointer(TestFileObject, NULL);
159     ok_eq_pointer(TestDeviceObject, NULL);
160     ok_eq_ulong(TestTestId, -1);
161 
162     TestWritten = FALSE;
163     TestDeviceObject = DeviceObject;
164     TestTestId = TestId;
165     TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject);
166     if (!skip(TestFileObject != NULL, "Failed to allocate FO\n"))
167     {
168         Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB));
169         if (!skip(Fcb != NULL, "ExAllocatePool failed\n"))
170         {
171             RtlZeroMemory(Fcb, sizeof(TEST_FCB));
172             ExInitializeFastMutex(&Fcb->HeaderMutex);
173             FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
174 
175             TestFileObject->FsContext = Fcb;
176             TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
177             Fcb->Header.AllocationSize.QuadPart = VACB_MAPPING_GRANULARITY;
178             Fcb->Header.FileSize.QuadPart = VACB_MAPPING_GRANULARITY - PAGE_SIZE;
179             Fcb->Header.ValidDataLength.QuadPart = VACB_MAPPING_GRANULARITY - PAGE_SIZE;
180 
181             if ((TestId > 1 && TestId < 4) || TestId == 5)
182             {
183                 Fcb->Header.AllocationSize.QuadPart = VACB_MAPPING_GRANULARITY - PAGE_SIZE;
184             }
185 
186             KmtStartSeh();
187             CcInitializeCacheMap(TestFileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, TRUE, &Callbacks, NULL);
188             KmtEndSeh(STATUS_SUCCESS);
189 
190             if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n"))
191             {
192                 trace("Starting test: %d\n", TestId);
193 
194                 if (TestId == 0 || TestId == 2)
195                 {
196                     Offset.QuadPart = 0;
197                     KmtStartSeh();
198                     Ret = CcMapData(TestFileObject, &Offset, VACB_MAPPING_GRANULARITY - PAGE_SIZE, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
199                     KmtEndSeh(STATUS_SUCCESS);
200 
201                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
202                     {
203                         ok_eq_ulong(Buffer[(VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
204                         CcUnpinData(Bcb);
205                     }
206 
207                     KmtStartSeh();
208                     CcSetFileSizes(TestFileObject, &NewFileSizes);
209                     KmtEndSeh(STATUS_SUCCESS);
210 
211                     Fcb->Header.AllocationSize.QuadPart = VACB_MAPPING_GRANULARITY;
212                     Fcb->Header.FileSize.QuadPart = VACB_MAPPING_GRANULARITY;
213 
214                     Offset.QuadPart = 0;
215                     KmtStartSeh();
216                     Ret = CcMapData(TestFileObject, &Offset, VACB_MAPPING_GRANULARITY, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
217                     KmtEndSeh(STATUS_SUCCESS);
218 
219                     if (!skip(Ret == TRUE, "CcMapData failed\n"))
220                     {
221                         ok_eq_ulong(Buffer[(VACB_MAPPING_GRANULARITY  - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
222 
223                         CcUnpinData(Bcb);
224                     }
225                 }
226                 else if (TestId == 1 || TestId == 3)
227                 {
228                     Buffer = ExAllocatePool(NonPagedPool, PAGE_SIZE);
229                     if (!skip(Buffer != NULL, "ExAllocatePool failed\n"))
230                     {
231                         Ret = FALSE;
232                         Offset.QuadPart = VACB_MAPPING_GRANULARITY - 2 * PAGE_SIZE;
233 
234                         KmtStartSeh();
235                         Ret = CcCopyRead(TestFileObject, &Offset, PAGE_SIZE, TRUE, Buffer, &IoStatus);
236                         KmtEndSeh(STATUS_SUCCESS);
237 
238                         ok_eq_ulong(Buffer[(PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
239 
240                         KmtStartSeh();
241                         CcSetFileSizes(TestFileObject, &NewFileSizes);
242                         KmtEndSeh(STATUS_SUCCESS);
243 
244                         Fcb->Header.AllocationSize.QuadPart = VACB_MAPPING_GRANULARITY;
245                         Fcb->Header.FileSize.QuadPart = VACB_MAPPING_GRANULARITY;
246                         RtlZeroMemory(Buffer, PAGE_SIZE);
247 
248                         Offset.QuadPart = VACB_MAPPING_GRANULARITY - PAGE_SIZE;
249 
250                         KmtStartSeh();
251                         Ret = CcCopyRead(TestFileObject, &Offset, PAGE_SIZE, TRUE, Buffer, &IoStatus);
252                         KmtEndSeh(STATUS_SUCCESS);
253 
254                         ok_eq_ulong(Buffer[(PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
255 
256                         ExFreePool(Buffer);
257                     }
258                 }
259                 else if (TestId == 4 || TestId == 5)
260                 {
261                     /* Kill lazy writer */
262                     CcSetAdditionalCacheAttributes(TestFileObject, FALSE, TRUE);
263 
264                     Offset.QuadPart = 0;
265                     KmtStartSeh();
266                     Ret = CcPinRead(TestFileObject, &Offset, VACB_MAPPING_GRANULARITY - PAGE_SIZE, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
267                     KmtEndSeh(STATUS_SUCCESS);
268 
269                     if (!skip(Ret == TRUE, "CcPinRead failed\n"))
270                     {
271                         LARGE_INTEGER Flushed;
272 
273                         ok_eq_ulong(Buffer[(VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
274                         Buffer[(VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)] = 0xDADADADA;
275 
276                         TestDirtying = TRUE;
277                         CcSetDirtyPinnedData(Bcb, NULL);
278                         TestDirtying = FALSE;
279 
280                         ok_bool_false(TestWritten, "Dirty VACB has been unexpectedly written!\n");
281 
282                         TestSizing = TRUE;
283                         KmtStartSeh();
284                         CcSetFileSizes(TestFileObject, &NewFileSizes);
285                         KmtEndSeh(STATUS_SUCCESS);
286                         TestSizing = FALSE;
287 
288                         ok_bool_false(TestWritten, "Dirty VACB has been unexpectedly written!\n");
289 
290                         Fcb->Header.AllocationSize.QuadPart = VACB_MAPPING_GRANULARITY;
291                         Fcb->Header.FileSize.QuadPart = VACB_MAPPING_GRANULARITY;
292 
293                         Flushed = CcGetFlushedValidData(TestFileObject->SectionObjectPointer, FALSE);
294                         ok(Flushed.QuadPart == 0, "Flushed: %I64d\n", Flushed.QuadPart);
295 
296                         TestUnpin = TRUE;
297                         CcUnpinData(Bcb);
298                         TestUnpin = FALSE;
299 
300                         ok_bool_false(TestWritten, "Dirty VACB has been unexpectedly written!\n");
301 
302                         Offset.QuadPart = 0;
303                         KmtStartSeh();
304                         Ret = CcMapData(TestFileObject, &Offset, VACB_MAPPING_GRANULARITY, MAP_WAIT, &Bcb, (PVOID *)&Buffer);
305                         KmtEndSeh(STATUS_SUCCESS);
306 
307                         if (!skip(Ret == TRUE, "CcMapData failed\n"))
308                         {
309                             ok_eq_ulong(Buffer[(VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG)) / sizeof(ULONG)], 0xDADADADA);
310                             ok_eq_ulong(Buffer[(VACB_MAPPING_GRANULARITY  - sizeof(ULONG)) / sizeof(ULONG)], 0xBABABABA);
311 
312                             CcUnpinData(Bcb);
313 
314                             ok_bool_false(TestWritten, "Dirty VACB has been unexpectedly written!\n");
315                         }
316                     }
317                 }
318             }
319         }
320     }
321 }
322 
323 
324 static
325 VOID
326 CleanupTest(
327     ULONG TestId,
328     PDEVICE_OBJECT DeviceObject)
329 {
330     LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL);
331     CACHE_UNINITIALIZE_EVENT CacheUninitEvent;
332 
333     ok_eq_pointer(TestDeviceObject, DeviceObject);
334     ok_eq_ulong(TestTestId, TestId);
335 
336     if (!skip(TestFileObject != NULL, "No test FO\n"))
337     {
338         if (CcIsFileCached(TestFileObject))
339         {
340             TestUncaching = TRUE;
341             KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE);
342             CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent);
343             KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL);
344             TestUncaching = FALSE;
345         }
346 
347         if (TestFileObject->FsContext != NULL)
348         {
349             ExFreePool(TestFileObject->FsContext);
350             TestFileObject->FsContext = NULL;
351             TestFileObject->SectionObjectPointer = NULL;
352         }
353 
354         ObDereferenceObject(TestFileObject);
355     }
356 
357     TestFileObject = NULL;
358     TestDeviceObject = NULL;
359     TestTestId = -1;
360 }
361 
362 
363 static
364 NTSTATUS
365 TestMessageHandler(
366     _In_ PDEVICE_OBJECT DeviceObject,
367     _In_ ULONG ControlCode,
368     _In_opt_ PVOID Buffer,
369     _In_ SIZE_T InLength,
370     _Inout_ PSIZE_T OutLength)
371 {
372     NTSTATUS Status = STATUS_SUCCESS;
373 
374     FsRtlEnterFileSystem();
375 
376     switch (ControlCode)
377     {
378         case IOCTL_START_TEST:
379             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
380             PerformTest(*(PULONG)Buffer, DeviceObject);
381             break;
382 
383         case IOCTL_FINISH_TEST:
384             ok_eq_ulong((ULONG)InLength, sizeof(ULONG));
385             CleanupTest(*(PULONG)Buffer, DeviceObject);
386             break;
387 
388         default:
389             Status = STATUS_NOT_IMPLEMENTED;
390             break;
391     }
392 
393     FsRtlExitFileSystem();
394 
395     return Status;
396 }
397 
398 static
399 NTSTATUS
400 TestIrpHandler(
401     _In_ PDEVICE_OBJECT DeviceObject,
402     _In_ PIRP Irp,
403     _In_ PIO_STACK_LOCATION IoStack)
404 {
405     NTSTATUS Status;
406 
407     PAGED_CODE();
408 
409     DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction);
410     ASSERT(IoStack->MajorFunction == IRP_MJ_READ ||
411            IoStack->MajorFunction == IRP_MJ_WRITE);
412 
413     FsRtlEnterFileSystem();
414 
415     Status = STATUS_NOT_SUPPORTED;
416     Irp->IoStatus.Information = 0;
417 
418     if (IoStack->MajorFunction == IRP_MJ_READ)
419     {
420         PMDL Mdl;
421         ULONG Length;
422         PTEST_FCB Fcb;
423         LARGE_INTEGER Offset;
424         PVOID Buffer, OrigBuffer;
425 
426         Offset = IoStack->Parameters.Read.ByteOffset;
427         Length = IoStack->Parameters.Read.Length;
428         Fcb = IoStack->FileObject->FsContext;
429 
430         ok_eq_pointer(DeviceObject, TestDeviceObject);
431         ok_eq_pointer(IoStack->FileObject, TestFileObject);
432         ok(Fcb != NULL, "Null FCB\n");
433 
434         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
435 
436         ok_irql(APC_LEVEL);
437         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
438         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
439 
440         ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n");
441         OrigBuffer = Buffer = MapAndLockUserBuffer(Irp, Length);
442         ok(Buffer != NULL, "Null pointer!\n");
443 
444         if (Offset.QuadPart < Fcb->Header.FileSize.QuadPart)
445         {
446             RtlFillMemory(Buffer, min(Length, Fcb->Header.FileSize.QuadPart - Offset.QuadPart), 0xBA);
447             Buffer = (PVOID)((ULONG_PTR)Buffer + (ULONG_PTR)min(Length, Fcb->Header.FileSize.QuadPart - Offset.QuadPart));
448 
449             if (Length > (Fcb->Header.FileSize.QuadPart - Offset.QuadPart))
450             {
451                 RtlFillMemory(Buffer, Length - Fcb->Header.FileSize.QuadPart, 0xBD);
452             }
453         }
454         else
455         {
456             RtlFillMemory(Buffer, Length, 0xBD);
457         }
458 
459         if ((TestTestId == 4 || TestTestId == 5) && TestWritten &&
460             Offset.QuadPart <= VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG) &&
461             Offset.QuadPart + Length >= VACB_MAPPING_GRANULARITY - PAGE_SIZE)
462         {
463             Buffer = (PVOID)((ULONG_PTR)OrigBuffer + (VACB_MAPPING_GRANULARITY - PAGE_SIZE - sizeof(ULONG)));
464             RtlFillMemory(Buffer, sizeof(ULONG), 0xDA);
465         }
466 
467         Status = STATUS_SUCCESS;
468 
469         Mdl = Irp->MdlAddress;
470         ok(Mdl != NULL, "Null pointer for MDL!\n");
471         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
472         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
473         ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n");
474         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
475 
476         Irp->IoStatus.Information = Length;
477     }
478     else if (IoStack->MajorFunction == IRP_MJ_WRITE)
479     {
480         PMDL Mdl;
481         ULONG Length;
482         PVOID Buffer;
483         LARGE_INTEGER Offset;
484 
485         Offset = IoStack->Parameters.Write.ByteOffset;
486         Length = IoStack->Parameters.Write.Length;
487 
488         ok((TestTestId == 4 || TestTestId == 5), "Unexpected test id: %d\n", TestTestId);
489         ok_eq_pointer(DeviceObject, TestDeviceObject);
490         ok_eq_pointer(IoStack->FileObject, TestFileObject);
491 
492         ok_bool_false(TestUnpin, "Write triggered while unpinning!\n");
493         ok_bool_false(TestSizing, "Write triggered while sizing!\n");
494         ok_bool_false(TestDirtying, "Write triggered while dirtying!\n");
495         ok_bool_true(TestUncaching, "Write not triggered while uncaching!\n");
496 
497         ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n");
498 
499         ok_irql(PASSIVE_LEVEL);
500         ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart);
501         ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length);
502 
503         Buffer = MapAndLockUserBuffer(Irp, Length);
504         ok(Buffer != NULL, "Null pointer!\n");
505 
506         Mdl = Irp->MdlAddress;
507         ok(Mdl != NULL, "Null pointer for MDL!\n");
508         ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n");
509         ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n");
510         ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n");
511 
512         TestWritten = TRUE;
513         Status = STATUS_SUCCESS;
514         Irp->IoStatus.Information = Length;
515     }
516 
517     if (Status == STATUS_PENDING)
518     {
519         IoMarkIrpPending(Irp);
520         IoCompleteRequest(Irp, IO_NO_INCREMENT);
521         Status = STATUS_PENDING;
522     }
523     else
524     {
525         Irp->IoStatus.Status = Status;
526         IoCompleteRequest(Irp, IO_NO_INCREMENT);
527     }
528 
529     FsRtlExitFileSystem();
530 
531     return Status;
532 }
533