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 /* Map after EOF */ 375 Ret = FALSE; 376 Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000; 377 378 KmtStartSeh(); 379 Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer); 380 KmtEndSeh(STATUS_SUCCESS); 381 ok(Ret == FALSE, "CcMapData succeed\n"); 382 383 if (Ret) 384 { 385 CcUnpinData(Bcb); 386 } 387 388 /* Map a VACB after EOF */ 389 Ret = FALSE; 390 Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY; 391 392 KmtStartSeh(); 393 Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer); 394 KmtEndSeh(STATUS_ACCESS_VIOLATION); 395 ok(Ret == FALSE, "CcMapData succeed\n"); 396 397 if (Ret) 398 { 399 CcUnpinData(Bcb); 400 } 401 402 /* Map more than a VACB */ 403 Ret = FALSE; 404 Offset.QuadPart = 0x0; 405 406 KmtStartSeh(); 407 Ret = CcMapData(TestFileObject, &Offset, 0x1000 + VACB_MAPPING_GRANULARITY, 0, &Bcb, (PVOID *)&Buffer); 408 KmtEndSeh(STATUS_SUCCESS); 409 ok(Ret == FALSE, "CcMapData succeed\n"); 410 411 if (Ret) 412 { 413 CcUnpinData(Bcb); 414 } 415 } 416 } 417 } 418 } 419 } 420 421 422 static 423 VOID 424 CleanupTest( 425 ULONG TestId, 426 PDEVICE_OBJECT DeviceObject) 427 { 428 LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL); 429 CACHE_UNINITIALIZE_EVENT CacheUninitEvent; 430 431 ok_eq_pointer(TestDeviceObject, DeviceObject); 432 ok_eq_ulong(TestTestId, TestId); 433 434 if (!skip(TestFileObject != NULL, "No test FO\n")) 435 { 436 if (CcIsFileCached(TestFileObject)) 437 { 438 KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE); 439 CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent); 440 KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL); 441 } 442 443 if (TestFileObject->FsContext != NULL) 444 { 445 ExFreePool(TestFileObject->FsContext); 446 TestFileObject->FsContext = NULL; 447 TestFileObject->SectionObjectPointer = NULL; 448 } 449 450 ObDereferenceObject(TestFileObject); 451 } 452 453 TestFileObject = NULL; 454 TestDeviceObject = NULL; 455 TestTestId = -1; 456 } 457 458 459 static 460 NTSTATUS 461 TestMessageHandler( 462 _In_ PDEVICE_OBJECT DeviceObject, 463 _In_ ULONG ControlCode, 464 _In_opt_ PVOID Buffer, 465 _In_ SIZE_T InLength, 466 _Inout_ PSIZE_T OutLength) 467 { 468 NTSTATUS Status = STATUS_SUCCESS; 469 470 FsRtlEnterFileSystem(); 471 472 switch (ControlCode) 473 { 474 case IOCTL_START_TEST: 475 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 476 PerformTest(*(PULONG)Buffer, DeviceObject); 477 break; 478 479 case IOCTL_FINISH_TEST: 480 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 481 CleanupTest(*(PULONG)Buffer, DeviceObject); 482 break; 483 484 default: 485 Status = STATUS_NOT_IMPLEMENTED; 486 break; 487 } 488 489 FsRtlExitFileSystem(); 490 491 return Status; 492 } 493 494 static 495 NTSTATUS 496 TestIrpHandler( 497 _In_ PDEVICE_OBJECT DeviceObject, 498 _In_ PIRP Irp, 499 _In_ PIO_STACK_LOCATION IoStack) 500 { 501 NTSTATUS Status; 502 503 PAGED_CODE(); 504 505 DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction); 506 ASSERT(IoStack->MajorFunction == IRP_MJ_READ); 507 508 FsRtlEnterFileSystem(); 509 510 Status = STATUS_NOT_SUPPORTED; 511 Irp->IoStatus.Information = 0; 512 513 if (IoStack->MajorFunction == IRP_MJ_READ) 514 { 515 PMDL Mdl; 516 ULONG Length; 517 PVOID Buffer; 518 LARGE_INTEGER Offset; 519 520 Offset = IoStack->Parameters.Read.ByteOffset; 521 Length = IoStack->Parameters.Read.Length; 522 523 ok_eq_pointer(DeviceObject, TestDeviceObject); 524 ok_eq_pointer(IoStack->FileObject, TestFileObject); 525 526 ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n"); 527 528 ok_irql(APC_LEVEL); 529 ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart); 530 ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length); 531 532 ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n"); 533 Buffer = MapAndLockUserBuffer(Irp, Length); 534 ok(Buffer != NULL, "Null pointer!\n"); 535 RtlFillMemory(Buffer, Length, 0xBA); 536 537 Status = STATUS_SUCCESS; 538 if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000) 539 { 540 *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE; 541 } 542 543 Mdl = Irp->MdlAddress; 544 ok(Mdl != NULL, "Null pointer for MDL!\n"); 545 ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n"); 546 ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n"); 547 ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n"); 548 ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n"); 549 550 Irp->IoStatus.Information = Length; 551 } 552 553 if (Status == STATUS_PENDING) 554 { 555 IoMarkIrpPending(Irp); 556 IoCompleteRequest(Irp, IO_NO_INCREMENT); 557 Status = STATUS_PENDING; 558 } 559 else 560 { 561 Irp->IoStatus.Status = Status; 562 IoCompleteRequest(Irp, IO_NO_INCREMENT); 563 } 564 565 FsRtlExitFileSystem(); 566 567 return Status; 568 } 569