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