[KMTESTS:CC] Add tests showing a dirty VACB isn't flushed on file growing
[reactos.git] / modules / rostests / kmtests / ntos_cc / CcSetFileSizes_drv.c
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 && 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 }