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 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"CcMapData"; 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 MapInAnotherThread(IN PVOID Context) 151 { 152 BOOLEAN Ret; 153 PULONG Buffer; 154 PVOID Bcb; 155 LARGE_INTEGER Offset; 156 PTEST_CONTEXT TestContext; 157 158 ok(TestFileObject != NULL, "Called in invalid context!\n"); 159 ok_eq_ulong(TestTestId, 3); 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 = 0x1000; 169 KmtStartSeh(); 170 Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer); 171 KmtEndSeh(STATUS_SUCCESS); 172 173 if (!skip(Ret == TRUE, "CcMapData failed\n")) 174 { 175 ok_eq_pointer(Bcb, TestContext->Bcb); 176 ok_eq_pointer(Buffer, TestContext->Buffer); 177 178 CcUnpinData(Bcb); 179 } 180 181 KmtStartSeh(); 182 Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, 0, &Bcb, (PVOID *)&Buffer); 183 KmtEndSeh(STATUS_SUCCESS); 184 185 if (!skip(Ret == TRUE, "CcPinRead failed\n")) 186 { 187 ok(Bcb != TestContext->Bcb, "Returned same BCB!\n"); 188 ok_eq_pointer(Buffer, TestContext->Buffer); 189 190 CcUnpinData(Bcb); 191 } 192 193 KmtStartSeh(); 194 Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_IF_BCB, &Bcb, (PVOID *)&Buffer); 195 KmtEndSeh(STATUS_SUCCESS); 196 ok(Ret == FALSE, "CcPinRead succeed\n"); 197 198 if (Ret) 199 { 200 CcUnpinData(Bcb); 201 } 202 203 KmtStartSeh(); 204 Ret = CcPinRead(TestFileObject, &Offset, TestContext->Length, PIN_EXCLUSIVE, &Bcb, (PVOID *)&Buffer); 205 KmtEndSeh(STATUS_SUCCESS); 206 207 if (!skip(Ret == TRUE, "CcPinRead failed\n")) 208 { 209 ok(Bcb != TestContext->Bcb, "Returned same BCB!\n"); 210 ok_eq_pointer(Buffer, TestContext->Buffer); 211 212 CcUnpinData(Bcb); 213 } 214 215 Offset.QuadPart = 0x1500; 216 TestContext->Length -= 0x500; 217 218 KmtStartSeh(); 219 Ret = CcMapData(TestFileObject, &Offset, TestContext->Length, MAP_WAIT, &Bcb, (PVOID *)&Buffer); 220 KmtEndSeh(STATUS_SUCCESS); 221 222 if (!skip(Ret == TRUE, "CcMapData failed\n")) 223 { 224 ok_eq_pointer(Bcb, TestContext->Bcb); 225 ok_eq_pointer(Buffer, (PVOID)((ULONG_PTR)TestContext->Buffer + 0x500)); 226 227 CcUnpinData(Bcb); 228 } 229 230 return; 231 } 232 233 static 234 VOID 235 PerformTest( 236 ULONG TestId, 237 PDEVICE_OBJECT DeviceObject) 238 { 239 PVOID Bcb; 240 BOOLEAN Ret; 241 PULONG Buffer; 242 PTEST_FCB Fcb; 243 LARGE_INTEGER Offset; 244 245 ok_eq_pointer(TestFileObject, NULL); 246 ok_eq_pointer(TestDeviceObject, NULL); 247 ok_eq_ulong(TestTestId, -1); 248 249 TestDeviceObject = DeviceObject; 250 TestTestId = TestId; 251 TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject); 252 if (!skip(TestFileObject != NULL, "Failed to allocate FO\n")) 253 { 254 Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB)); 255 if (!skip(Fcb != NULL, "ExAllocatePool failed\n")) 256 { 257 RtlZeroMemory(Fcb, sizeof(TEST_FCB)); 258 ExInitializeFastMutex(&Fcb->HeaderMutex); 259 FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex); 260 261 TestFileObject->FsContext = Fcb; 262 TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; 263 264 KmtStartSeh(); 265 CcInitializeCacheMap(TestFileObject, &FileSizes, FALSE, &Callbacks, NULL); 266 KmtEndSeh(STATUS_SUCCESS); 267 268 if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n")) 269 { 270 if (TestId < 3) 271 { 272 Ret = FALSE; 273 Offset.QuadPart = TestId * 0x1000; 274 KmtStartSeh(); 275 Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer); 276 KmtEndSeh(STATUS_SUCCESS); 277 278 if (!skip(Ret == TRUE, "CcMapData failed\n")) 279 { 280 ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE); 281 282 CcUnpinData(Bcb); 283 } 284 } 285 else if (TestId == 3) 286 { 287 PTEST_CONTEXT TestContext; 288 289 TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT)); 290 if (!skip(TestContext != NULL, "ExAllocatePool failed\n")) 291 { 292 Ret = FALSE; 293 Offset.QuadPart = 0x1000; 294 KmtStartSeh(); 295 Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &TestContext->Bcb, &TestContext->Buffer); 296 KmtEndSeh(STATUS_SUCCESS); 297 298 if (!skip(Ret == TRUE, "CcMapData failed\n")) 299 { 300 PKTHREAD ThreadHandle; 301 302 #ifdef _X86_ 303 /* FIXME: Should be fixed, will fail under certains conditions */ 304 ok(TestContext->Buffer > (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF, 305 "Buffer %p not mapped in system space\n", TestContext->Buffer); 306 #else 307 #ifdef _M_AMD64 308 ok(TestContext->Buffer > (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000, 309 "Buffer %p not mapped in system space\n", TestContext->Buffer); 310 #else 311 skip(FALSE, "System space mapping not defined\n"); 312 #endif 313 #endif 314 315 TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart; 316 ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext); 317 KmtFinishThread(ThreadHandle, NULL); 318 319 TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart; 320 ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext); 321 KmtFinishThread(ThreadHandle, NULL); 322 323 CcUnpinData(TestContext->Bcb); 324 } 325 326 ExFreePool(TestContext); 327 } 328 } 329 else if (TestId == 4) 330 { 331 /* Map after EOF */ 332 Ret = FALSE; 333 Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000; 334 335 KmtStartSeh(); 336 Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer); 337 KmtEndSeh(STATUS_SUCCESS); 338 ok(Ret == FALSE, "CcMapData succeed\n"); 339 340 if (Ret) 341 { 342 CcUnpinData(Bcb); 343 } 344 345 /* Map a VACB after EOF */ 346 Ret = FALSE; 347 Offset.QuadPart = FileSizes.FileSize.QuadPart + 0x1000 + VACB_MAPPING_GRANULARITY; 348 349 KmtStartSeh(); 350 Ret = CcMapData(TestFileObject, &Offset, 0x1000, 0, &Bcb, (PVOID *)&Buffer); 351 KmtEndSeh(STATUS_ACCESS_VIOLATION); 352 ok(Ret == FALSE, "CcMapData succeed\n"); 353 354 if (Ret) 355 { 356 CcUnpinData(Bcb); 357 } 358 359 /* Map more than a VACB */ 360 Ret = FALSE; 361 Offset.QuadPart = 0x0; 362 363 KmtStartSeh(); 364 Ret = CcMapData(TestFileObject, &Offset, 0x1000 + VACB_MAPPING_GRANULARITY, 0, &Bcb, (PVOID *)&Buffer); 365 KmtEndSeh(STATUS_SUCCESS); 366 ok(Ret == FALSE, "CcMapData succeed\n"); 367 368 if (Ret) 369 { 370 CcUnpinData(Bcb); 371 } 372 } 373 } 374 } 375 } 376 } 377 378 379 static 380 VOID 381 CleanupTest( 382 ULONG TestId, 383 PDEVICE_OBJECT DeviceObject) 384 { 385 LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL); 386 CACHE_UNINITIALIZE_EVENT CacheUninitEvent; 387 388 ok_eq_pointer(TestDeviceObject, DeviceObject); 389 ok_eq_ulong(TestTestId, TestId); 390 391 if (!skip(TestFileObject != NULL, "No test FO\n")) 392 { 393 if (CcIsFileCached(TestFileObject)) 394 { 395 KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE); 396 CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent); 397 KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL); 398 } 399 400 if (TestFileObject->FsContext != NULL) 401 { 402 ExFreePool(TestFileObject->FsContext); 403 TestFileObject->FsContext = NULL; 404 TestFileObject->SectionObjectPointer = NULL; 405 } 406 407 ObDereferenceObject(TestFileObject); 408 } 409 410 TestFileObject = NULL; 411 TestDeviceObject = NULL; 412 TestTestId = -1; 413 } 414 415 416 static 417 NTSTATUS 418 TestMessageHandler( 419 _In_ PDEVICE_OBJECT DeviceObject, 420 _In_ ULONG ControlCode, 421 _In_opt_ PVOID Buffer, 422 _In_ SIZE_T InLength, 423 _Inout_ PSIZE_T OutLength) 424 { 425 NTSTATUS Status = STATUS_SUCCESS; 426 427 FsRtlEnterFileSystem(); 428 429 switch (ControlCode) 430 { 431 case IOCTL_START_TEST: 432 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 433 PerformTest(*(PULONG)Buffer, DeviceObject); 434 break; 435 436 case IOCTL_FINISH_TEST: 437 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 438 CleanupTest(*(PULONG)Buffer, DeviceObject); 439 break; 440 441 default: 442 Status = STATUS_NOT_IMPLEMENTED; 443 break; 444 } 445 446 FsRtlExitFileSystem(); 447 448 return Status; 449 } 450 451 static 452 NTSTATUS 453 TestIrpHandler( 454 _In_ PDEVICE_OBJECT DeviceObject, 455 _In_ PIRP Irp, 456 _In_ PIO_STACK_LOCATION IoStack) 457 { 458 NTSTATUS Status; 459 460 PAGED_CODE(); 461 462 DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction); 463 ASSERT(IoStack->MajorFunction == IRP_MJ_READ); 464 465 FsRtlEnterFileSystem(); 466 467 Status = STATUS_NOT_SUPPORTED; 468 Irp->IoStatus.Information = 0; 469 470 if (IoStack->MajorFunction == IRP_MJ_READ) 471 { 472 PMDL Mdl; 473 ULONG Length; 474 PVOID Buffer; 475 LARGE_INTEGER Offset; 476 477 Offset = IoStack->Parameters.Read.ByteOffset; 478 Length = IoStack->Parameters.Read.Length; 479 480 ok_eq_pointer(DeviceObject, TestDeviceObject); 481 ok_eq_pointer(IoStack->FileObject, TestFileObject); 482 483 ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n"); 484 485 ok_irql(APC_LEVEL); 486 ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart); 487 ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length); 488 489 ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n"); 490 Buffer = MapAndLockUserBuffer(Irp, Length); 491 ok(Buffer != NULL, "Null pointer!\n"); 492 RtlFillMemory(Buffer, Length, 0xBA); 493 494 Status = STATUS_SUCCESS; 495 if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000) 496 { 497 *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE; 498 } 499 500 Mdl = Irp->MdlAddress; 501 ok(Mdl != NULL, "Null pointer for MDL!\n"); 502 ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n"); 503 ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n"); 504 ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n"); 505 ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n"); 506 507 Irp->IoStatus.Information = Length; 508 } 509 510 if (Status == STATUS_PENDING) 511 { 512 IoMarkIrpPending(Irp); 513 IoCompleteRequest(Irp, IO_NO_INCREMENT); 514 Status = STATUS_PENDING; 515 } 516 else 517 { 518 Irp->IoStatus.Status = Status; 519 IoCompleteRequest(Irp, IO_NO_INCREMENT); 520 } 521 522 FsRtlExitFileSystem(); 523 524 return Status; 525 } 526