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 return; 216 } 217 218 static 219 VOID 220 PerformTest( 221 ULONG TestId, 222 PDEVICE_OBJECT DeviceObject) 223 { 224 PVOID Bcb; 225 BOOLEAN Ret; 226 PULONG Buffer; 227 PTEST_FCB Fcb; 228 LARGE_INTEGER Offset; 229 230 ok_eq_pointer(TestFileObject, NULL); 231 ok_eq_pointer(TestDeviceObject, NULL); 232 ok_eq_ulong(TestTestId, -1); 233 234 TestDeviceObject = DeviceObject; 235 TestTestId = TestId; 236 TestFileObject = IoCreateStreamFileObject(NULL, DeviceObject); 237 if (!skip(TestFileObject != NULL, "Failed to allocate FO\n")) 238 { 239 Fcb = ExAllocatePool(NonPagedPool, sizeof(TEST_FCB)); 240 if (!skip(Fcb != NULL, "ExAllocatePool failed\n")) 241 { 242 RtlZeroMemory(Fcb, sizeof(TEST_FCB)); 243 ExInitializeFastMutex(&Fcb->HeaderMutex); 244 FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex); 245 246 TestFileObject->FsContext = Fcb; 247 TestFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; 248 249 KmtStartSeh(); 250 CcInitializeCacheMap(TestFileObject, &FileSizes, FALSE, &Callbacks, NULL); 251 KmtEndSeh(STATUS_SUCCESS); 252 253 if (!skip(CcIsFileCached(TestFileObject) == TRUE, "CcInitializeCacheMap failed\n")) 254 { 255 if (TestId < 3) 256 { 257 Ret = FALSE; 258 Offset.QuadPart = TestId * 0x1000; 259 KmtStartSeh(); 260 Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &Bcb, (PVOID *)&Buffer); 261 KmtEndSeh(STATUS_SUCCESS); 262 263 if (!skip(Ret == TRUE, "CcMapData failed\n")) 264 { 265 ok_eq_ulong(Buffer[(0x3000 - TestId * 0x1000) / sizeof(ULONG)], 0xDEADBABE); 266 267 CcUnpinData(Bcb); 268 } 269 } 270 else if (TestId == 3) 271 { 272 PTEST_CONTEXT TestContext; 273 274 TestContext = ExAllocatePool(NonPagedPool, sizeof(TEST_CONTEXT)); 275 if (!skip(Fcb != NULL, "ExAllocatePool failed\n")) 276 { 277 Ret = FALSE; 278 Offset.QuadPart = 0x1000; 279 KmtStartSeh(); 280 Ret = CcMapData(TestFileObject, &Offset, FileSizes.FileSize.QuadPart - Offset.QuadPart, MAP_WAIT, &TestContext->Bcb, &TestContext->Buffer); 281 KmtEndSeh(STATUS_SUCCESS); 282 283 if (!skip(Ret == TRUE, "CcMapData failed\n")) 284 { 285 PKTHREAD ThreadHandle; 286 287 #ifdef _X86_ 288 /* FIXME: Should be fixed, will fail under certains conditions */ 289 ok(TestContext->Buffer > (PVOID)0xC1000000 && TestContext->Buffer < (PVOID)0xDCFFFFFF, 290 "Buffer %p not mapped in system space\n", TestContext->Buffer); 291 #else 292 #ifdef _M_AMD64 293 ok(TestContext->Buffer > (PVOID)0xFFFFF98000000000 && TestContext->Buffer < (PVOID)0xFFFFFA8000000000, 294 "Buffer %p not mapped in system space\n", TestContext->Buffer); 295 #else 296 skip(FALSE, "System space mapping not defined\n"); 297 #endif 298 #endif 299 300 TestContext->Length = FileSizes.FileSize.QuadPart - Offset.QuadPart; 301 ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext); 302 KmtFinishThread(ThreadHandle, NULL); 303 304 TestContext->Length = FileSizes.FileSize.QuadPart - 2 * Offset.QuadPart; 305 ThreadHandle = KmtStartThread(MapInAnotherThread, TestContext); 306 KmtFinishThread(ThreadHandle, NULL); 307 308 CcUnpinData(TestContext->Bcb); 309 } 310 311 ExFreePool(TestContext); 312 } 313 } 314 } 315 } 316 } 317 } 318 319 320 static 321 VOID 322 CleanupTest( 323 ULONG TestId, 324 PDEVICE_OBJECT DeviceObject) 325 { 326 LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL); 327 CACHE_UNINITIALIZE_EVENT CacheUninitEvent; 328 329 ok_eq_pointer(TestDeviceObject, DeviceObject); 330 ok_eq_ulong(TestTestId, TestId); 331 332 if (!skip(TestFileObject != NULL, "No test FO\n")) 333 { 334 if (CcIsFileCached(TestFileObject)) 335 { 336 KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE); 337 CcUninitializeCacheMap(TestFileObject, &Zero, &CacheUninitEvent); 338 KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL); 339 } 340 341 if (TestFileObject->FsContext != NULL) 342 { 343 ExFreePool(TestFileObject->FsContext); 344 TestFileObject->FsContext = NULL; 345 TestFileObject->SectionObjectPointer = NULL; 346 } 347 348 ObDereferenceObject(TestFileObject); 349 } 350 351 TestFileObject = NULL; 352 TestDeviceObject = NULL; 353 TestTestId = -1; 354 } 355 356 357 static 358 NTSTATUS 359 TestMessageHandler( 360 _In_ PDEVICE_OBJECT DeviceObject, 361 _In_ ULONG ControlCode, 362 _In_opt_ PVOID Buffer, 363 _In_ SIZE_T InLength, 364 _Inout_ PSIZE_T OutLength) 365 { 366 NTSTATUS Status = STATUS_SUCCESS; 367 368 FsRtlEnterFileSystem(); 369 370 switch (ControlCode) 371 { 372 case IOCTL_START_TEST: 373 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 374 PerformTest(*(PULONG)Buffer, DeviceObject); 375 break; 376 377 case IOCTL_FINISH_TEST: 378 ok_eq_ulong((ULONG)InLength, sizeof(ULONG)); 379 CleanupTest(*(PULONG)Buffer, DeviceObject); 380 break; 381 382 default: 383 Status = STATUS_NOT_IMPLEMENTED; 384 break; 385 } 386 387 FsRtlExitFileSystem(); 388 389 return Status; 390 } 391 392 static 393 NTSTATUS 394 TestIrpHandler( 395 _In_ PDEVICE_OBJECT DeviceObject, 396 _In_ PIRP Irp, 397 _In_ PIO_STACK_LOCATION IoStack) 398 { 399 NTSTATUS Status; 400 401 PAGED_CODE(); 402 403 DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction); 404 ASSERT(IoStack->MajorFunction == IRP_MJ_READ); 405 406 FsRtlEnterFileSystem(); 407 408 Status = STATUS_NOT_SUPPORTED; 409 Irp->IoStatus.Information = 0; 410 411 if (IoStack->MajorFunction == IRP_MJ_READ) 412 { 413 PMDL Mdl; 414 ULONG Length; 415 PVOID Buffer; 416 LARGE_INTEGER Offset; 417 418 Offset = IoStack->Parameters.Read.ByteOffset; 419 Length = IoStack->Parameters.Read.Length; 420 421 ok_eq_pointer(DeviceObject, TestDeviceObject); 422 ok_eq_pointer(IoStack->FileObject, TestFileObject); 423 424 ok(FlagOn(Irp->Flags, IRP_NOCACHE), "Not coming from Cc\n"); 425 426 ok_irql(APC_LEVEL); 427 ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart); 428 ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length); 429 430 ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n"); 431 Buffer = MapAndLockUserBuffer(Irp, Length); 432 ok(Buffer != NULL, "Null pointer!\n"); 433 RtlFillMemory(Buffer, Length, 0xBA); 434 435 Status = STATUS_SUCCESS; 436 if (Offset.QuadPart <= 0x3000 && Offset.QuadPart + Length > 0x3000) 437 { 438 *(PULONG)((ULONG_PTR)Buffer + (ULONG_PTR)(0x3000 - Offset.QuadPart)) = 0xDEADBABE; 439 } 440 441 Mdl = Irp->MdlAddress; 442 ok(Mdl != NULL, "Null pointer for MDL!\n"); 443 ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n"); 444 ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n"); 445 ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n"); 446 ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n"); 447 448 Irp->IoStatus.Information = Length; 449 } 450 451 if (Status == STATUS_PENDING) 452 { 453 IoMarkIrpPending(Irp); 454 IoCompleteRequest(Irp, IO_NO_INCREMENT); 455 Status = STATUS_PENDING; 456 } 457 else 458 { 459 Irp->IoStatus.Status = Status; 460 IoCompleteRequest(Irp, IO_NO_INCREMENT); 461 } 462 463 FsRtlExitFileSystem(); 464 465 return Status; 466 } 467