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