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