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