1 /* 2 * PROJECT: ReactOS kernel-mode tests 3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory 4 * PURPOSE: Test driver for CcCopyRead 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 typedef struct _TEST_FCB 14 { 15 FSRTL_ADVANCED_FCB_HEADER Header; 16 SECTION_OBJECT_POINTERS SectionObjectPointers; 17 FAST_MUTEX HeaderMutex; 18 BOOLEAN BigFile; 19 } TEST_FCB, *PTEST_FCB; 20 21 static PFILE_OBJECT TestFileObject; 22 static PDEVICE_OBJECT TestDeviceObject; 23 static KMT_IRP_HANDLER TestIrpHandler; 24 static FAST_IO_DISPATCH TestFastIoDispatch; 25 static BOOLEAN InBehaviourTest; 26 27 BOOLEAN ReadCalledNonCached; 28 LARGE_INTEGER ReadOffset; 29 ULONG ReadLength; 30 31 static 32 BOOLEAN 33 NTAPI 34 FastIoRead( 35 _In_ PFILE_OBJECT FileObject, 36 _In_ PLARGE_INTEGER FileOffset, 37 _In_ ULONG Length, 38 _In_ BOOLEAN Wait, 39 _In_ ULONG LockKey, 40 _Out_ PVOID Buffer, 41 _Out_ PIO_STATUS_BLOCK IoStatus, 42 _In_ PDEVICE_OBJECT DeviceObject) 43 { 44 IoStatus->Status = STATUS_NOT_SUPPORTED; 45 return FALSE; 46 } 47 48 NTSTATUS 49 TestEntry( 50 _In_ PDRIVER_OBJECT DriverObject, 51 _In_ PCUNICODE_STRING RegistryPath, 52 _Out_ PCWSTR *DeviceName, 53 _Inout_ INT *Flags) 54 { 55 NTSTATUS Status = STATUS_SUCCESS; 56 57 PAGED_CODE(); 58 59 UNREFERENCED_PARAMETER(RegistryPath); 60 61 *DeviceName = L"CcCopyRead"; 62 *Flags = TESTENTRY_NO_EXCLUSIVE_DEVICE | 63 TESTENTRY_BUFFERED_IO_DEVICE | 64 TESTENTRY_NO_READONLY_DEVICE; 65 66 KmtRegisterIrpHandler(IRP_MJ_CLEANUP, NULL, TestIrpHandler); 67 KmtRegisterIrpHandler(IRP_MJ_CREATE, NULL, TestIrpHandler); 68 KmtRegisterIrpHandler(IRP_MJ_READ, NULL, TestIrpHandler); 69 70 TestFastIoDispatch.FastIoRead = FastIoRead; 71 DriverObject->FastIoDispatch = &TestFastIoDispatch; 72 73 74 return Status; 75 } 76 77 VOID 78 TestUnload( 79 _In_ PDRIVER_OBJECT DriverObject) 80 { 81 PAGED_CODE(); 82 } 83 84 BOOLEAN 85 NTAPI 86 AcquireForLazyWrite( 87 _In_ PVOID Context, 88 _In_ BOOLEAN Wait) 89 { 90 return TRUE; 91 } 92 93 VOID 94 NTAPI 95 ReleaseFromLazyWrite( 96 _In_ PVOID Context) 97 { 98 return; 99 } 100 101 BOOLEAN 102 NTAPI 103 AcquireForReadAhead( 104 _In_ PVOID Context, 105 _In_ BOOLEAN Wait) 106 { 107 return TRUE; 108 } 109 110 VOID 111 NTAPI 112 ReleaseFromReadAhead( 113 _In_ PVOID Context) 114 { 115 return; 116 } 117 118 static CACHE_MANAGER_CALLBACKS Callbacks = { 119 AcquireForLazyWrite, 120 ReleaseFromLazyWrite, 121 AcquireForReadAhead, 122 ReleaseFromReadAhead, 123 }; 124 125 static 126 PVOID 127 MapAndLockUserBuffer( 128 _In_ _Out_ PIRP Irp, 129 _In_ ULONG BufferLength) 130 { 131 PMDL Mdl; 132 133 if (Irp->MdlAddress == NULL) 134 { 135 Mdl = IoAllocateMdl(Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp); 136 if (Mdl == NULL) 137 { 138 return NULL; 139 } 140 141 _SEH2_TRY 142 { 143 MmProbeAndLockPages(Mdl, Irp->RequestorMode, IoWriteAccess); 144 } 145 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 146 { 147 IoFreeMdl(Mdl); 148 Irp->MdlAddress = NULL; 149 _SEH2_YIELD(return NULL); 150 } 151 _SEH2_END; 152 } 153 154 return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 155 } 156 157 static 158 void 159 reset_read(void) 160 { 161 ReadCalledNonCached = FALSE; 162 ReadOffset.QuadPart = MAXLONGLONG; 163 ReadLength = MAXULONG; 164 } 165 166 #define ok_read_called(_Offset, _Length) do { \ 167 ok(ReadCalledNonCached, "CcCopyRead should have triggerred a non-cached read.\n"); \ 168 ok_eq_longlong(ReadOffset.QuadPart, _Offset); \ 169 ok_eq_ulong(ReadLength, _Length); \ 170 }while(0) 171 172 #define ok_read_not_called() ok(!ReadCalledNonCached, "CcCopyRead shouldn't have triggered a read.\n") 173 174 static 175 VOID 176 Test_CcCopyRead(PFILE_OBJECT FileObject) 177 { 178 179 BOOLEAN Ret; 180 LARGE_INTEGER Offset; 181 CHAR Buffer[10]; 182 IO_STATUS_BLOCK IoStatus; 183 184 memset(Buffer, 0xAC, 10); 185 186 /* Test bogus file object & file offset */ 187 Ret = 'x'; 188 KmtStartSeh() 189 Ret = CcCopyRead(FileObject, NULL, 0, FALSE, NULL, &IoStatus); 190 KmtEndSeh(STATUS_ACCESS_VIOLATION); 191 ok_eq_char(Ret, 'x'); 192 193 Ret = 'x'; 194 Offset.QuadPart = 0; 195 KmtStartSeh() 196 Ret = CcCopyRead(NULL, &Offset, 10, FALSE, Buffer, &IoStatus); 197 KmtEndSeh(STATUS_ACCESS_VIOLATION); 198 ok_eq_char(Ret, 'x'); 199 200 /* What happens on invalid buffer */ 201 Ret = 'x'; 202 Offset.QuadPart = 0; 203 memset(&IoStatus, 0xAB, sizeof(IoStatus)); 204 reset_read(); 205 KmtStartSeh() 206 Ret = CcCopyRead(FileObject, &Offset, 0, TRUE, NULL, &IoStatus); 207 KmtEndSeh(STATUS_SUCCESS); 208 ok_bool_true(Ret, "CcCopyRead(0, NULL) should succeed\n"); 209 /* When there is nothing to write, there is no reason to read */ 210 ok_read_not_called(); 211 ok_eq_hex(IoStatus.Status, STATUS_SUCCESS); 212 ok_eq_ulongptr(IoStatus.Information, 0); 213 214 Ret = 'x'; 215 Offset.QuadPart = 0; 216 memset(&IoStatus, 0xAB, sizeof(IoStatus)); 217 reset_read(); 218 KmtStartSeh() 219 Ret = CcCopyRead(FileObject, &Offset, 10, TRUE, NULL, &IoStatus); 220 KmtEndSeh(STATUS_INVALID_USER_BUFFER); 221 ok_eq_char(Ret, 'x'); 222 /* This raises an exception, but it actually triggered a read */ 223 ok_read_called(0, PAGE_SIZE); 224 ok_eq_hex(IoStatus.Status, 0xABABABAB); 225 ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0xABABABABABABABAB); 226 227 /* So this one succeeds, as the page is now resident */ 228 Ret = 'x'; 229 Offset.QuadPart = 0; 230 memset(&IoStatus, 0xAB, sizeof(IoStatus)); 231 reset_read(); 232 KmtStartSeh() 233 Ret = CcCopyRead(FileObject, &Offset, 10, FALSE, Buffer, &IoStatus); 234 KmtEndSeh(STATUS_SUCCESS); 235 ok_bool_true(Ret, "CcCopyRead should succeed\n"); 236 /* But there was no read triggered, as the page is already resident. */ 237 ok_read_not_called(); 238 ok_eq_hex(IoStatus.Status, STATUS_SUCCESS); 239 ok_eq_ulongptr(IoStatus.Information, 10); 240 241 /* But this one doesn't */ 242 Ret = 'x'; 243 Offset.QuadPart = PAGE_SIZE; 244 memset(&IoStatus, 0xAB, sizeof(IoStatus)); 245 reset_read(); 246 KmtStartSeh() 247 Ret = CcCopyRead(FileObject, &Offset, 10, FALSE, Buffer, &IoStatus); 248 KmtEndSeh(STATUS_SUCCESS); 249 ok_bool_false(Ret, "CcCopyRead should fail\n"); 250 /* But it triggered a read anyway. */ 251 ok_read_called(PAGE_SIZE, PAGE_SIZE); 252 ok_eq_hex(IoStatus.Status, 0xABABABAB); 253 ok_eq_ulongptr(IoStatus.Information, (ULONG_PTR)0xABABABABABABABAB); 254 255 /* Of course, waiting for it succeeds and triggers the read */ 256 Ret = 'x'; 257 Offset.QuadPart = PAGE_SIZE * 2; 258 memset(&IoStatus, 0xAB, sizeof(IoStatus)); 259 reset_read(); 260 KmtStartSeh() 261 Ret = CcCopyRead(FileObject, &Offset, 10, TRUE, Buffer, &IoStatus); 262 KmtEndSeh(STATUS_SUCCESS); 263 ok_bool_true(Ret, "CcCopyRead should succeed\n"); 264 ok_read_called(PAGE_SIZE * 2, PAGE_SIZE); 265 ok_eq_hex(IoStatus.Status, STATUS_SUCCESS); 266 ok_eq_ulongptr(IoStatus.Information, 10); 267 268 /* Try the same without a status block */ 269 Ret = 'x'; 270 Offset.QuadPart = PAGE_SIZE * 2; 271 KmtStartSeh() 272 Ret = CcCopyRead(FileObject, &Offset, 10, TRUE, Buffer, NULL); 273 KmtEndSeh(STATUS_ACCESS_VIOLATION); 274 ok_eq_char(Ret, 'x'); 275 } 276 277 278 static 279 NTSTATUS 280 TestIrpHandler( 281 _In_ PDEVICE_OBJECT DeviceObject, 282 _In_ PIRP Irp, 283 _In_ PIO_STACK_LOCATION IoStack) 284 { 285 LARGE_INTEGER Zero = RTL_CONSTANT_LARGE_INTEGER(0LL); 286 NTSTATUS Status; 287 PTEST_FCB Fcb; 288 CACHE_UNINITIALIZE_EVENT CacheUninitEvent; 289 290 PAGED_CODE(); 291 292 DPRINT("IRP %x/%x\n", IoStack->MajorFunction, IoStack->MinorFunction); 293 ASSERT(IoStack->MajorFunction == IRP_MJ_CLEANUP || 294 IoStack->MajorFunction == IRP_MJ_CREATE || 295 IoStack->MajorFunction == IRP_MJ_READ); 296 297 Status = STATUS_NOT_SUPPORTED; 298 Irp->IoStatus.Information = 0; 299 300 if (IoStack->MajorFunction == IRP_MJ_CREATE) 301 { 302 ok_irql(PASSIVE_LEVEL); 303 304 if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR)) 305 { 306 TestDeviceObject = DeviceObject; 307 TestFileObject = IoStack->FileObject; 308 } 309 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Fcb), 'FwrI'); 310 RtlZeroMemory(Fcb, sizeof(*Fcb)); 311 ExInitializeFastMutex(&Fcb->HeaderMutex); 312 FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex); 313 Fcb->BigFile = FALSE; 314 if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) && 315 IoStack->FileObject->FileName.Buffer[1] == 'B') 316 { 317 Fcb->Header.AllocationSize.QuadPart = 1000000; 318 Fcb->Header.FileSize.QuadPart = 1000000; 319 Fcb->Header.ValidDataLength.QuadPart = 1000000; 320 } 321 else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) && 322 IoStack->FileObject->FileName.Buffer[1] == 'S') 323 { 324 Fcb->Header.AllocationSize.QuadPart = 1004; 325 Fcb->Header.FileSize.QuadPart = 1004; 326 Fcb->Header.ValidDataLength.QuadPart = 1004; 327 } 328 else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) && 329 IoStack->FileObject->FileName.Buffer[1] == 'R') 330 { 331 Fcb->Header.AllocationSize.QuadPart = 62; 332 Fcb->Header.FileSize.QuadPart = 62; 333 Fcb->Header.ValidDataLength.QuadPart = 62; 334 } 335 else if (IoStack->FileObject->FileName.Length >= 2 * sizeof(WCHAR) && 336 IoStack->FileObject->FileName.Buffer[1] == 'F') 337 { 338 Fcb->Header.AllocationSize.QuadPart = 4294967296; 339 Fcb->Header.FileSize.QuadPart = 4294967296; 340 Fcb->Header.ValidDataLength.QuadPart = 4294967296; 341 Fcb->BigFile = TRUE; 342 } 343 else 344 { 345 Fcb->Header.AllocationSize.QuadPart = 512; 346 Fcb->Header.FileSize.QuadPart = 512; 347 Fcb->Header.ValidDataLength.QuadPart = 512; 348 } 349 Fcb->Header.IsFastIoPossible = FastIoIsNotPossible; 350 IoStack->FileObject->FsContext = Fcb; 351 IoStack->FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers; 352 353 CcInitializeCacheMap(IoStack->FileObject, 354 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, 355 FALSE, &Callbacks, NULL); 356 357 Irp->IoStatus.Information = FILE_OPENED; 358 Status = STATUS_SUCCESS; 359 } 360 else if (IoStack->MajorFunction == IRP_MJ_READ) 361 { 362 BOOLEAN Ret; 363 ULONG Length; 364 PVOID Buffer; 365 LARGE_INTEGER Offset; 366 static const UNICODE_STRING BehaviourTestFileName = RTL_CONSTANT_STRING(L"\\BehaviourTestFile"); 367 368 Offset = IoStack->Parameters.Read.ByteOffset; 369 Length = IoStack->Parameters.Read.Length; 370 Fcb = IoStack->FileObject->FsContext; 371 372 ok_eq_pointer(DeviceObject, TestDeviceObject); 373 ok_eq_pointer(IoStack->FileObject, TestFileObject); 374 375 /* Check special file name */ 376 InBehaviourTest = RtlCompareUnicodeString(&IoStack->FileObject->FileName, &BehaviourTestFileName, TRUE) == 0; 377 378 if (!FlagOn(Irp->Flags, IRP_NOCACHE)) 379 { 380 ok_irql(PASSIVE_LEVEL); 381 382 if (InBehaviourTest) 383 { 384 Test_CcCopyRead(IoStack->FileObject); 385 Status = Irp->IoStatus.Status = STATUS_SUCCESS; 386 } 387 else 388 { 389 /* We don't want to test alignement for big files (not the purpose of the test) */ 390 if (!Fcb->BigFile) 391 { 392 ok(Offset.QuadPart % PAGE_SIZE != 0, "Offset is aligned: %I64i\n", Offset.QuadPart); 393 ok(Length % PAGE_SIZE != 0, "Length is aligned: %I64i\n", Length); 394 } 395 396 Buffer = Irp->AssociatedIrp.SystemBuffer; 397 ok(Buffer != NULL, "Null pointer!\n"); 398 399 _SEH2_TRY 400 { 401 Ret = CcCopyRead(IoStack->FileObject, &Offset, Length, TRUE, Buffer, 402 &Irp->IoStatus); 403 ok_bool_true(Ret, "CcCopyRead"); 404 } 405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 406 { 407 Irp->IoStatus.Status = _SEH2_GetExceptionCode(); 408 } 409 _SEH2_END; 410 411 Status = Irp->IoStatus.Status; 412 413 if (NT_SUCCESS(Status)) 414 { 415 if (Offset.QuadPart <= 1000LL && Offset.QuadPart + Length > 1000LL) 416 { 417 ok_eq_hex(*(PUSHORT)((ULONG_PTR)Buffer + (ULONG_PTR)(1000LL - Offset.QuadPart)), 0xFFFF); 418 } 419 else 420 { 421 ok_eq_hex(*(PUSHORT)Buffer, 0xBABA); 422 } 423 } 424 } 425 } 426 else 427 { 428 PMDL Mdl; 429 430 ReadCalledNonCached = TRUE; 431 ReadOffset = Offset; 432 ReadLength = Length; 433 434 ok_irql(APC_LEVEL); 435 ok((Offset.QuadPart % PAGE_SIZE == 0 || Offset.QuadPart == 0), "Offset is not aligned: %I64i\n", Offset.QuadPart); 436 ok(Length % PAGE_SIZE == 0, "Length is not aligned: %I64i\n", Length); 437 438 ok(Irp->AssociatedIrp.SystemBuffer == NULL, "A SystemBuffer was allocated!\n"); 439 Buffer = MapAndLockUserBuffer(Irp, Length); 440 ok(Buffer != NULL, "Null pointer!\n"); 441 RtlFillMemory(Buffer, Length, 0xBA); 442 443 Status = STATUS_SUCCESS; 444 if (Offset.QuadPart <= 1000LL && Offset.QuadPart + Length > 1000LL) 445 { 446 *(PUSHORT)((ULONG_PTR)Buffer + (ULONG_PTR)(1000LL - Offset.QuadPart)) = 0xFFFF; 447 } 448 449 Mdl = Irp->MdlAddress; 450 ok(Mdl != NULL, "Null pointer for MDL!\n"); 451 ok((Mdl->MdlFlags & MDL_PAGES_LOCKED) != 0, "MDL not locked\n"); 452 ok((Mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) == 0, "MDL from non paged\n"); 453 ok((Mdl->MdlFlags & MDL_IO_PAGE_READ) != 0, "Non paging IO\n"); 454 ok((Irp->Flags & IRP_PAGING_IO) != 0, "Non paging IO\n"); 455 } 456 457 if (NT_SUCCESS(Status)) 458 { 459 Irp->IoStatus.Information = Length; 460 IoStack->FileObject->CurrentByteOffset.QuadPart = Offset.QuadPart + Length; 461 } 462 463 InBehaviourTest = FALSE; 464 } 465 else if (IoStack->MajorFunction == IRP_MJ_CLEANUP) 466 { 467 ok_irql(PASSIVE_LEVEL); 468 KeInitializeEvent(&CacheUninitEvent.Event, NotificationEvent, FALSE); 469 CcUninitializeCacheMap(IoStack->FileObject, &Zero, &CacheUninitEvent); 470 KeWaitForSingleObject(&CacheUninitEvent.Event, Executive, KernelMode, FALSE, NULL); 471 Fcb = IoStack->FileObject->FsContext; 472 ExFreePoolWithTag(Fcb, 'FwrI'); 473 IoStack->FileObject->FsContext = NULL; 474 Status = STATUS_SUCCESS; 475 } 476 477 if (Status == STATUS_PENDING) 478 { 479 IoMarkIrpPending(Irp); 480 IoCompleteRequest(Irp, IO_NO_INCREMENT); 481 Status = STATUS_PENDING; 482 } 483 else 484 { 485 Irp->IoStatus.Status = Status; 486 IoCompleteRequest(Irp, IO_NO_INCREMENT); 487 } 488 489 return Status; 490 } 491