1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/cc/copy.c 5 * PURPOSE: Implements cache managers copy interface 6 * 7 * PROGRAMMERS: Some people? 8 * Pierre Schweitzer (pierre@reactos.org) 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS *******************************************************************/ 18 19 static PFN_NUMBER CcZeroPage = 0; 20 21 #define MAX_ZERO_LENGTH (256 * 1024) 22 23 typedef enum _CC_COPY_OPERATION 24 { 25 CcOperationRead, 26 CcOperationWrite, 27 CcOperationZero 28 } CC_COPY_OPERATION; 29 30 typedef enum _CC_CAN_WRITE_RETRY 31 { 32 FirstTry = 0, 33 RetryAllowRemote = 253, 34 RetryForceCheckPerFile = 254, 35 RetryMasterLocked = 255, 36 } CC_CAN_WRITE_RETRY; 37 38 ULONG CcRosTraceLevel = 0; 39 ULONG CcFastMdlReadWait; 40 ULONG CcFastMdlReadNotPossible; 41 ULONG CcFastReadNotPossible; 42 ULONG CcFastReadWait; 43 ULONG CcFastReadNoWait; 44 ULONG CcFastReadResourceMiss; 45 46 /* Counters: 47 * - Amount of pages flushed to the disk 48 * - Number of flush operations 49 */ 50 ULONG CcDataPages = 0; 51 ULONG CcDataFlushes = 0; 52 53 /* FUNCTIONS *****************************************************************/ 54 55 VOID 56 NTAPI 57 MiZeroPhysicalPage ( 58 IN PFN_NUMBER PageFrameIndex 59 ); 60 61 VOID 62 NTAPI 63 CcInitCacheZeroPage ( 64 VOID) 65 { 66 NTSTATUS Status; 67 68 MI_SET_USAGE(MI_USAGE_CACHE); 69 //MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); 70 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage); 71 if (!NT_SUCCESS(Status)) 72 { 73 DbgPrint("Can't allocate CcZeroPage.\n"); 74 KeBugCheck(CACHE_MANAGER); 75 } 76 MiZeroPhysicalPage(CcZeroPage); 77 } 78 79 NTSTATUS 80 NTAPI 81 CcReadVirtualAddress ( 82 PROS_VACB Vacb) 83 { 84 ULONG Size; 85 PMDL Mdl; 86 NTSTATUS Status; 87 IO_STATUS_BLOCK IoStatus; 88 KEVENT Event; 89 ULARGE_INTEGER LargeSize; 90 91 LargeSize.QuadPart = Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart; 92 if (LargeSize.QuadPart > VACB_MAPPING_GRANULARITY) 93 { 94 LargeSize.QuadPart = VACB_MAPPING_GRANULARITY; 95 } 96 Size = LargeSize.LowPart; 97 98 Size = ROUND_TO_PAGES(Size); 99 ASSERT(Size <= VACB_MAPPING_GRANULARITY); 100 ASSERT(Size > 0); 101 102 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL); 103 if (!Mdl) 104 { 105 return STATUS_INSUFFICIENT_RESOURCES; 106 } 107 108 Status = STATUS_SUCCESS; 109 _SEH2_TRY 110 { 111 MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess); 112 } 113 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) 114 { 115 Status = _SEH2_GetExceptionCode(); 116 DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress); 117 KeBugCheck(CACHE_MANAGER); 118 } _SEH2_END; 119 120 if (NT_SUCCESS(Status)) 121 { 122 Mdl->MdlFlags |= MDL_IO_PAGE_READ; 123 KeInitializeEvent(&Event, NotificationEvent, FALSE); 124 Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus); 125 if (Status == STATUS_PENDING) 126 { 127 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 128 Status = IoStatus.Status; 129 } 130 131 MmUnlockPages(Mdl); 132 } 133 134 IoFreeMdl(Mdl); 135 136 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE)) 137 { 138 DPRINT1("IoPageRead failed, Status %x\n", Status); 139 return Status; 140 } 141 142 if (Size < VACB_MAPPING_GRANULARITY) 143 { 144 RtlZeroMemory((char*)Vacb->BaseAddress + Size, 145 VACB_MAPPING_GRANULARITY - Size); 146 } 147 148 return STATUS_SUCCESS; 149 } 150 151 NTSTATUS 152 NTAPI 153 CcWriteVirtualAddress ( 154 PROS_VACB Vacb) 155 { 156 ULONG Size; 157 PMDL Mdl; 158 NTSTATUS Status; 159 IO_STATUS_BLOCK IoStatus; 160 KEVENT Event; 161 ULARGE_INTEGER LargeSize; 162 163 LargeSize.QuadPart = Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart; 164 if (LargeSize.QuadPart > VACB_MAPPING_GRANULARITY) 165 { 166 LargeSize.QuadPart = VACB_MAPPING_GRANULARITY; 167 } 168 Size = LargeSize.LowPart; 169 // 170 // Nonpaged pool PDEs in ReactOS must actually be synchronized between the 171 // MmGlobalPageDirectory and the real system PDE directory. What a mess... 172 // 173 { 174 ULONG i = 0; 175 do 176 { 177 MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i << PAGE_SHIFT))); 178 } while (++i < (Size >> PAGE_SHIFT)); 179 } 180 181 Size = ROUND_TO_PAGES(Size); 182 ASSERT(Size <= VACB_MAPPING_GRANULARITY); 183 ASSERT(Size > 0); 184 185 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL); 186 if (!Mdl) 187 { 188 return STATUS_INSUFFICIENT_RESOURCES; 189 } 190 191 Status = STATUS_SUCCESS; 192 _SEH2_TRY 193 { 194 MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess); 195 } 196 _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) 197 { 198 Status = _SEH2_GetExceptionCode(); 199 DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress); 200 KeBugCheck(CACHE_MANAGER); 201 } _SEH2_END; 202 203 if (NT_SUCCESS(Status)) 204 { 205 KeInitializeEvent(&Event, NotificationEvent, FALSE); 206 Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus); 207 if (Status == STATUS_PENDING) 208 { 209 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 210 Status = IoStatus.Status; 211 } 212 213 MmUnlockPages(Mdl); 214 } 215 IoFreeMdl(Mdl); 216 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE)) 217 { 218 DPRINT1("IoPageWrite failed, Status %x\n", Status); 219 return Status; 220 } 221 222 return STATUS_SUCCESS; 223 } 224 225 NTSTATUS 226 ReadWriteOrZero( 227 _Inout_ PVOID BaseAddress, 228 _Inout_opt_ PVOID Buffer, 229 _In_ ULONG Length, 230 _In_ CC_COPY_OPERATION Operation) 231 { 232 NTSTATUS Status = STATUS_SUCCESS; 233 234 if (Operation == CcOperationZero) 235 { 236 /* Zero */ 237 RtlZeroMemory(BaseAddress, Length); 238 } 239 else 240 { 241 _SEH2_TRY 242 { 243 if (Operation == CcOperationWrite) 244 RtlCopyMemory(BaseAddress, Buffer, Length); 245 else 246 RtlCopyMemory(Buffer, BaseAddress, Length); 247 } 248 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 249 { 250 Status = _SEH2_GetExceptionCode(); 251 } 252 _SEH2_END; 253 } 254 return Status; 255 } 256 257 BOOLEAN 258 CcCopyData ( 259 _In_ PFILE_OBJECT FileObject, 260 _In_ LONGLONG FileOffset, 261 _Inout_ PVOID Buffer, 262 _In_ LONGLONG Length, 263 _In_ CC_COPY_OPERATION Operation, 264 _In_ BOOLEAN Wait, 265 _Out_ PIO_STATUS_BLOCK IoStatus) 266 { 267 NTSTATUS Status; 268 LONGLONG CurrentOffset; 269 ULONG BytesCopied; 270 KIRQL OldIrql; 271 PROS_SHARED_CACHE_MAP SharedCacheMap; 272 PLIST_ENTRY ListEntry; 273 PROS_VACB Vacb; 274 ULONG PartialLength; 275 PVOID BaseAddress; 276 BOOLEAN Valid; 277 PPRIVATE_CACHE_MAP PrivateCacheMap; 278 279 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 280 PrivateCacheMap = FileObject->PrivateCacheMap; 281 CurrentOffset = FileOffset; 282 BytesCopied = 0; 283 284 if (!Wait) 285 { 286 /* test if the requested data is available */ 287 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql); 288 /* FIXME: this loop doesn't take into account areas that don't have 289 * a VACB in the list yet */ 290 ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink; 291 while (ListEntry != &SharedCacheMap->CacheMapVacbListHead) 292 { 293 Vacb = CONTAINING_RECORD(ListEntry, 294 ROS_VACB, 295 CacheMapVacbListEntry); 296 ListEntry = ListEntry->Flink; 297 if (!Vacb->Valid && 298 DoRangesIntersect(Vacb->FileOffset.QuadPart, 299 VACB_MAPPING_GRANULARITY, 300 CurrentOffset, Length)) 301 { 302 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql); 303 /* data not available */ 304 return FALSE; 305 } 306 if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length) 307 break; 308 } 309 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql); 310 } 311 312 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY; 313 if (PartialLength != 0) 314 { 315 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength); 316 Status = CcRosRequestVacb(SharedCacheMap, 317 ROUND_DOWN(CurrentOffset, 318 VACB_MAPPING_GRANULARITY), 319 &BaseAddress, 320 &Valid, 321 &Vacb); 322 if (!NT_SUCCESS(Status)) 323 ExRaiseStatus(Status); 324 if (!Valid) 325 { 326 Status = CcReadVirtualAddress(Vacb); 327 if (!NT_SUCCESS(Status)) 328 { 329 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 330 ExRaiseStatus(Status); 331 } 332 } 333 Status = ReadWriteOrZero((PUCHAR)BaseAddress + CurrentOffset % VACB_MAPPING_GRANULARITY, 334 Buffer, 335 PartialLength, 336 Operation); 337 338 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE); 339 340 if (!NT_SUCCESS(Status)) 341 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 342 343 Length -= PartialLength; 344 CurrentOffset += PartialLength; 345 BytesCopied += PartialLength; 346 347 if (Operation != CcOperationZero) 348 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength); 349 } 350 351 while (Length > 0) 352 { 353 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0); 354 PartialLength = min(VACB_MAPPING_GRANULARITY, Length); 355 Status = CcRosRequestVacb(SharedCacheMap, 356 CurrentOffset, 357 &BaseAddress, 358 &Valid, 359 &Vacb); 360 if (!NT_SUCCESS(Status)) 361 ExRaiseStatus(Status); 362 if (!Valid && 363 (Operation == CcOperationRead || 364 PartialLength < VACB_MAPPING_GRANULARITY)) 365 { 366 Status = CcReadVirtualAddress(Vacb); 367 if (!NT_SUCCESS(Status)) 368 { 369 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 370 ExRaiseStatus(Status); 371 } 372 } 373 Status = ReadWriteOrZero(BaseAddress, Buffer, PartialLength, Operation); 374 375 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE); 376 377 if (!NT_SUCCESS(Status)) 378 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 379 380 Length -= PartialLength; 381 CurrentOffset += PartialLength; 382 BytesCopied += PartialLength; 383 384 if (Operation != CcOperationZero) 385 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength); 386 } 387 388 /* If that was a successful sync read operation, let's handle read ahead */ 389 if (Operation == CcOperationRead && Length == 0 && Wait) 390 { 391 /* If file isn't random access and next read may get us cross VACB boundary, 392 * schedule next read 393 */ 394 if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS) && 395 (CurrentOffset - 1) / VACB_MAPPING_GRANULARITY != (CurrentOffset + BytesCopied - 1) / VACB_MAPPING_GRANULARITY) 396 { 397 CcScheduleReadAhead(FileObject, (PLARGE_INTEGER)&FileOffset, BytesCopied); 398 } 399 400 /* And update read history in private cache map */ 401 PrivateCacheMap->FileOffset1.QuadPart = PrivateCacheMap->FileOffset2.QuadPart; 402 PrivateCacheMap->BeyondLastByte1.QuadPart = PrivateCacheMap->BeyondLastByte2.QuadPart; 403 PrivateCacheMap->FileOffset2.QuadPart = FileOffset; 404 PrivateCacheMap->BeyondLastByte2.QuadPart = FileOffset + BytesCopied; 405 } 406 407 IoStatus->Status = STATUS_SUCCESS; 408 IoStatus->Information = BytesCopied; 409 return TRUE; 410 } 411 412 VOID 413 CcPostDeferredWrites(VOID) 414 { 415 ULONG WrittenBytes; 416 417 /* We'll try to write as much as we can */ 418 WrittenBytes = 0; 419 while (TRUE) 420 { 421 KIRQL OldIrql; 422 PLIST_ENTRY ListEntry; 423 PDEFERRED_WRITE DeferredWrite; 424 425 DeferredWrite = NULL; 426 427 /* Lock our deferred writes list */ 428 KeAcquireSpinLock(&CcDeferredWriteSpinLock, &OldIrql); 429 for (ListEntry = CcDeferredWrites.Flink; 430 ListEntry != &CcDeferredWrites; 431 ListEntry = ListEntry->Flink) 432 { 433 /* Extract an entry */ 434 DeferredWrite = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks); 435 436 /* Compute the modified bytes, based on what we already wrote */ 437 WrittenBytes += DeferredWrite->BytesToWrite; 438 /* We overflowed, give up */ 439 if (WrittenBytes < DeferredWrite->BytesToWrite) 440 { 441 DeferredWrite = NULL; 442 break; 443 } 444 445 /* Check we can write */ 446 if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, RetryForceCheckPerFile)) 447 { 448 /* We can, so remove it from the list and stop looking for entry */ 449 RemoveEntryList(&DeferredWrite->DeferredWriteLinks); 450 break; 451 } 452 453 /* If we don't accept modified pages, stop here */ 454 if (!DeferredWrite->LimitModifiedPages) 455 { 456 DeferredWrite = NULL; 457 break; 458 } 459 460 /* Reset count as nothing was written yet */ 461 WrittenBytes -= DeferredWrite->BytesToWrite; 462 DeferredWrite = NULL; 463 } 464 KeReleaseSpinLock(&CcDeferredWriteSpinLock, OldIrql); 465 466 /* Nothing to write found, give up */ 467 if (DeferredWrite == NULL) 468 { 469 break; 470 } 471 472 /* If we have an event, set it and quit */ 473 if (DeferredWrite->Event) 474 { 475 KeSetEvent(DeferredWrite->Event, IO_NO_INCREMENT, FALSE); 476 } 477 /* Otherwise, call the write routine and free the context */ 478 else 479 { 480 DeferredWrite->PostRoutine(DeferredWrite->Context1, DeferredWrite->Context2); 481 ExFreePoolWithTag(DeferredWrite, 'CcDw'); 482 } 483 } 484 } 485 486 VOID 487 CcPerformReadAhead( 488 IN PFILE_OBJECT FileObject) 489 { 490 NTSTATUS Status; 491 LONGLONG CurrentOffset; 492 KIRQL OldIrql; 493 PROS_SHARED_CACHE_MAP SharedCacheMap; 494 PROS_VACB Vacb; 495 ULONG PartialLength; 496 PVOID BaseAddress; 497 BOOLEAN Valid; 498 ULONG Length; 499 PPRIVATE_CACHE_MAP PrivateCacheMap; 500 BOOLEAN Locked; 501 502 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 503 504 /* Critical: 505 * PrivateCacheMap might disappear in-between if the handle 506 * to the file is closed (private is attached to the handle not to 507 * the file), so we need to lock the master lock while we deal with 508 * it. It won't disappear without attempting to lock such lock. 509 */ 510 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 511 PrivateCacheMap = FileObject->PrivateCacheMap; 512 /* If the handle was closed since the read ahead was scheduled, just quit */ 513 if (PrivateCacheMap == NULL) 514 { 515 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 516 ObDereferenceObject(FileObject); 517 return; 518 } 519 /* Otherwise, extract read offset and length and release private map */ 520 else 521 { 522 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 523 CurrentOffset = PrivateCacheMap->ReadAheadOffset[1].QuadPart; 524 Length = PrivateCacheMap->ReadAheadLength[1]; 525 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 526 } 527 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 528 529 /* Time to go! */ 530 DPRINT("Doing ReadAhead for %p\n", FileObject); 531 /* Lock the file, first */ 532 if (!SharedCacheMap->Callbacks->AcquireForReadAhead(SharedCacheMap->LazyWriteContext, FALSE)) 533 { 534 Locked = FALSE; 535 goto Clear; 536 } 537 538 /* Remember it's locked */ 539 Locked = TRUE; 540 541 /* Don't read past the end of the file */ 542 if (CurrentOffset >= SharedCacheMap->FileSize.QuadPart) 543 { 544 goto Clear; 545 } 546 if (CurrentOffset + Length > SharedCacheMap->FileSize.QuadPart) 547 { 548 Length = SharedCacheMap->FileSize.QuadPart - CurrentOffset; 549 } 550 551 /* Next of the algorithm will lock like CcCopyData with the slight 552 * difference that we don't copy data back to an user-backed buffer 553 * We just bring data into Cc 554 */ 555 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY; 556 if (PartialLength != 0) 557 { 558 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength); 559 Status = CcRosRequestVacb(SharedCacheMap, 560 ROUND_DOWN(CurrentOffset, 561 VACB_MAPPING_GRANULARITY), 562 &BaseAddress, 563 &Valid, 564 &Vacb); 565 if (!NT_SUCCESS(Status)) 566 { 567 DPRINT1("Failed to request VACB: %lx!\n", Status); 568 goto Clear; 569 } 570 571 if (!Valid) 572 { 573 Status = CcReadVirtualAddress(Vacb); 574 if (!NT_SUCCESS(Status)) 575 { 576 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 577 DPRINT1("Failed to read data: %lx!\n", Status); 578 goto Clear; 579 } 580 } 581 582 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 583 584 Length -= PartialLength; 585 CurrentOffset += PartialLength; 586 } 587 588 while (Length > 0) 589 { 590 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0); 591 PartialLength = min(VACB_MAPPING_GRANULARITY, Length); 592 Status = CcRosRequestVacb(SharedCacheMap, 593 CurrentOffset, 594 &BaseAddress, 595 &Valid, 596 &Vacb); 597 if (!NT_SUCCESS(Status)) 598 { 599 DPRINT1("Failed to request VACB: %lx!\n", Status); 600 goto Clear; 601 } 602 603 if (!Valid) 604 { 605 Status = CcReadVirtualAddress(Vacb); 606 if (!NT_SUCCESS(Status)) 607 { 608 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE); 609 DPRINT1("Failed to read data: %lx!\n", Status); 610 goto Clear; 611 } 612 } 613 614 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE); 615 616 Length -= PartialLength; 617 CurrentOffset += PartialLength; 618 } 619 620 Clear: 621 /* See previous comment about private cache map */ 622 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 623 PrivateCacheMap = FileObject->PrivateCacheMap; 624 if (PrivateCacheMap != NULL) 625 { 626 /* Mark read ahead as unactive */ 627 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 628 InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE); 629 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 630 } 631 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 632 633 /* If file was locked, release it */ 634 if (Locked) 635 { 636 SharedCacheMap->Callbacks->ReleaseFromReadAhead(SharedCacheMap->LazyWriteContext); 637 } 638 639 /* And drop our extra reference (See: CcScheduleReadAhead) */ 640 ObDereferenceObject(FileObject); 641 642 return; 643 } 644 645 /* 646 * @unimplemented 647 */ 648 BOOLEAN 649 NTAPI 650 CcCanIWrite ( 651 IN PFILE_OBJECT FileObject, 652 IN ULONG BytesToWrite, 653 IN BOOLEAN Wait, 654 IN BOOLEAN Retrying) 655 { 656 KIRQL OldIrql; 657 KEVENT WaitEvent; 658 ULONG Length, Pages; 659 BOOLEAN PerFileDefer; 660 DEFERRED_WRITE Context; 661 PFSRTL_COMMON_FCB_HEADER Fcb; 662 CC_CAN_WRITE_RETRY TryContext; 663 PROS_SHARED_CACHE_MAP SharedCacheMap; 664 665 CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n", 666 FileObject, BytesToWrite, Wait, Retrying); 667 668 /* Write through is always OK */ 669 if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH)) 670 { 671 return TRUE; 672 } 673 674 TryContext = Retrying; 675 /* Allow remote file if not from posted */ 676 if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote) 677 { 678 return TRUE; 679 } 680 681 /* Don't exceed max tolerated size */ 682 Length = MAX_ZERO_LENGTH; 683 if (BytesToWrite < MAX_ZERO_LENGTH) 684 { 685 Length = BytesToWrite; 686 } 687 688 Pages = BYTES_TO_PAGES(Length); 689 690 /* By default, assume limits per file won't be hit */ 691 PerFileDefer = FALSE; 692 Fcb = FileObject->FsContext; 693 /* Do we have to check for limits per file? */ 694 if (TryContext >= RetryForceCheckPerFile || 695 BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES)) 696 { 697 /* If master is not locked, lock it now */ 698 if (TryContext != RetryMasterLocked) 699 { 700 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 701 } 702 703 /* Let's not assume the file is cached... */ 704 if (FileObject->SectionObjectPointer != NULL && 705 FileObject->SectionObjectPointer->SharedCacheMap != NULL) 706 { 707 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 708 /* Do we have limits per file set? */ 709 if (SharedCacheMap->DirtyPageThreshold != 0 && 710 SharedCacheMap->DirtyPages != 0) 711 { 712 /* Yes, check whether they are blocking */ 713 if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold) 714 { 715 PerFileDefer = TRUE; 716 } 717 } 718 } 719 720 /* And don't forget to release master */ 721 if (TryContext != RetryMasterLocked) 722 { 723 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 724 } 725 } 726 727 /* So, now allow write if: 728 * - Not the first try or we have no throttling yet 729 * AND: 730 * - We don't exceed threshold! 731 * - We don't exceed what Mm can allow us to use 732 * + If we're above top, that's fine 733 * + If we're above bottom with limited modified pages, that's fine 734 * + Otherwise, throttle! 735 */ 736 if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) && 737 CcTotalDirtyPages + Pages < CcDirtyPageThreshold && 738 (MmAvailablePages > MmThrottleTop || 739 (MmModifiedPageListHead.Total < 1000 && MmAvailablePages > MmThrottleBottom)) && 740 !PerFileDefer) 741 { 742 return TRUE; 743 } 744 745 /* If we can wait, we'll start the wait loop for waiting till we can 746 * write for real 747 */ 748 if (!Wait) 749 { 750 return FALSE; 751 } 752 753 /* Otherwise, if there are no deferred writes yet, start the lazy writer */ 754 if (IsListEmpty(&CcDeferredWrites)) 755 { 756 KIRQL OldIrql; 757 758 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 759 CcScheduleLazyWriteScan(TRUE); 760 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 761 } 762 763 /* Initialize our wait event */ 764 KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE); 765 766 /* And prepare a dummy context */ 767 Context.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 768 Context.NodeByteSize = sizeof(DEFERRED_WRITE); 769 Context.FileObject = FileObject; 770 Context.BytesToWrite = BytesToWrite; 771 Context.LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 772 Context.Event = &WaitEvent; 773 774 /* And queue it */ 775 if (Retrying) 776 { 777 /* To the top, if that's a retry */ 778 ExInterlockedInsertHeadList(&CcDeferredWrites, 779 &Context.DeferredWriteLinks, 780 &CcDeferredWriteSpinLock); 781 } 782 else 783 { 784 /* To the bottom, if that's a first time */ 785 ExInterlockedInsertTailList(&CcDeferredWrites, 786 &Context.DeferredWriteLinks, 787 &CcDeferredWriteSpinLock); 788 } 789 790 DPRINT1("Actively deferring write for: %p\n", FileObject); 791 /* Now, we'll loop until our event is set. When it is set, it means that caller 792 * can immediately write, and has to 793 */ 794 do 795 { 796 CcPostDeferredWrites(); 797 } while (KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &CcIdleDelay) != STATUS_SUCCESS); 798 799 return TRUE; 800 } 801 802 /* 803 * @implemented 804 */ 805 BOOLEAN 806 NTAPI 807 CcCopyRead ( 808 IN PFILE_OBJECT FileObject, 809 IN PLARGE_INTEGER FileOffset, 810 IN ULONG Length, 811 IN BOOLEAN Wait, 812 OUT PVOID Buffer, 813 OUT PIO_STATUS_BLOCK IoStatus) 814 { 815 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n", 816 FileObject, FileOffset->QuadPart, Length, Wait); 817 818 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, " 819 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n", 820 FileObject, FileOffset->QuadPart, Length, Wait, 821 Buffer, IoStatus); 822 823 return CcCopyData(FileObject, 824 FileOffset->QuadPart, 825 Buffer, 826 Length, 827 CcOperationRead, 828 Wait, 829 IoStatus); 830 } 831 832 /* 833 * @implemented 834 */ 835 BOOLEAN 836 NTAPI 837 CcCopyWrite ( 838 IN PFILE_OBJECT FileObject, 839 IN PLARGE_INTEGER FileOffset, 840 IN ULONG Length, 841 IN BOOLEAN Wait, 842 IN PVOID Buffer) 843 { 844 IO_STATUS_BLOCK IoStatus; 845 846 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n", 847 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 848 849 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, " 850 "Length %lu, Wait %u, Buffer 0x%p)\n", 851 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 852 853 return CcCopyData(FileObject, 854 FileOffset->QuadPart, 855 Buffer, 856 Length, 857 CcOperationWrite, 858 Wait, 859 &IoStatus); 860 } 861 862 /* 863 * @implemented 864 */ 865 VOID 866 NTAPI 867 CcDeferWrite ( 868 IN PFILE_OBJECT FileObject, 869 IN PCC_POST_DEFERRED_WRITE PostRoutine, 870 IN PVOID Context1, 871 IN PVOID Context2, 872 IN ULONG BytesToWrite, 873 IN BOOLEAN Retrying) 874 { 875 KIRQL OldIrql; 876 PDEFERRED_WRITE Context; 877 PFSRTL_COMMON_FCB_HEADER Fcb; 878 879 CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n", 880 FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying); 881 882 /* Try to allocate a context for queueing the write operation */ 883 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEFERRED_WRITE), 'CcDw'); 884 /* If it failed, immediately execute the operation! */ 885 if (Context == NULL) 886 { 887 PostRoutine(Context1, Context2); 888 return; 889 } 890 891 Fcb = FileObject->FsContext; 892 893 /* Otherwise, initialize the context */ 894 RtlZeroMemory(Context, sizeof(DEFERRED_WRITE)); 895 Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 896 Context->NodeByteSize = sizeof(DEFERRED_WRITE); 897 Context->FileObject = FileObject; 898 Context->PostRoutine = PostRoutine; 899 Context->Context1 = Context1; 900 Context->Context2 = Context2; 901 Context->BytesToWrite = BytesToWrite; 902 Context->LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 903 904 /* And queue it */ 905 if (Retrying) 906 { 907 /* To the top, if that's a retry */ 908 ExInterlockedInsertHeadList(&CcDeferredWrites, 909 &Context->DeferredWriteLinks, 910 &CcDeferredWriteSpinLock); 911 } 912 else 913 { 914 /* To the bottom, if that's a first time */ 915 ExInterlockedInsertTailList(&CcDeferredWrites, 916 &Context->DeferredWriteLinks, 917 &CcDeferredWriteSpinLock); 918 } 919 920 /* Try to execute the posted writes */ 921 CcPostDeferredWrites(); 922 923 /* Schedule a lazy writer run to handle deferred writes */ 924 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 925 if (!LazyWriter.ScanActive) 926 { 927 CcScheduleLazyWriteScan(FALSE); 928 } 929 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 930 } 931 932 /* 933 * @unimplemented 934 */ 935 VOID 936 NTAPI 937 CcFastCopyRead ( 938 IN PFILE_OBJECT FileObject, 939 IN ULONG FileOffset, 940 IN ULONG Length, 941 IN ULONG PageCount, 942 OUT PVOID Buffer, 943 OUT PIO_STATUS_BLOCK IoStatus) 944 { 945 LARGE_INTEGER LargeFileOffset; 946 BOOLEAN Success; 947 948 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n", 949 FileObject, FileOffset, Length, PageCount, Buffer); 950 951 DBG_UNREFERENCED_PARAMETER(PageCount); 952 953 LargeFileOffset.QuadPart = FileOffset; 954 Success = CcCopyRead(FileObject, 955 &LargeFileOffset, 956 Length, 957 TRUE, 958 Buffer, 959 IoStatus); 960 ASSERT(Success == TRUE); 961 } 962 963 /* 964 * @unimplemented 965 */ 966 VOID 967 NTAPI 968 CcFastCopyWrite ( 969 IN PFILE_OBJECT FileObject, 970 IN ULONG FileOffset, 971 IN ULONG Length, 972 IN PVOID Buffer) 973 { 974 LARGE_INTEGER LargeFileOffset; 975 BOOLEAN Success; 976 977 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n", 978 FileObject, FileOffset, Length, Buffer); 979 980 LargeFileOffset.QuadPart = FileOffset; 981 Success = CcCopyWrite(FileObject, 982 &LargeFileOffset, 983 Length, 984 TRUE, 985 Buffer); 986 ASSERT(Success == TRUE); 987 } 988 989 /* 990 * @implemented 991 */ 992 BOOLEAN 993 NTAPI 994 CcZeroData ( 995 IN PFILE_OBJECT FileObject, 996 IN PLARGE_INTEGER StartOffset, 997 IN PLARGE_INTEGER EndOffset, 998 IN BOOLEAN Wait) 999 { 1000 NTSTATUS Status; 1001 LARGE_INTEGER WriteOffset; 1002 LONGLONG Length; 1003 ULONG CurrentLength; 1004 PMDL Mdl; 1005 ULONG i; 1006 IO_STATUS_BLOCK Iosb; 1007 KEVENT Event; 1008 1009 CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n", 1010 FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait); 1011 1012 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, " 1013 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart, 1014 Wait); 1015 1016 Length = EndOffset->QuadPart - StartOffset->QuadPart; 1017 WriteOffset.QuadPart = StartOffset->QuadPart; 1018 1019 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL) 1020 { 1021 /* File is not cached */ 1022 1023 Mdl = _alloca(MmSizeOfMdl(NULL, MAX_ZERO_LENGTH)); 1024 1025 while (Length > 0) 1026 { 1027 if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH) 1028 { 1029 CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE; 1030 } 1031 else 1032 { 1033 CurrentLength = Length; 1034 } 1035 MmInitializeMdl(Mdl, (PVOID)(ULONG_PTR)WriteOffset.QuadPart, CurrentLength); 1036 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ); 1037 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++) 1038 { 1039 ((PPFN_NUMBER)(Mdl + 1))[i] = CcZeroPage; 1040 } 1041 KeInitializeEvent(&Event, NotificationEvent, FALSE); 1042 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb); 1043 if (Status == STATUS_PENDING) 1044 { 1045 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 1046 Status = Iosb.Status; 1047 } 1048 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) 1049 { 1050 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); 1051 } 1052 if (!NT_SUCCESS(Status)) 1053 { 1054 return FALSE; 1055 } 1056 WriteOffset.QuadPart += CurrentLength; 1057 Length -= CurrentLength; 1058 } 1059 } 1060 else 1061 { 1062 IO_STATUS_BLOCK IoStatus; 1063 1064 return CcCopyData(FileObject, 1065 WriteOffset.QuadPart, 1066 NULL, 1067 Length, 1068 CcOperationZero, 1069 Wait, 1070 &IoStatus); 1071 } 1072 1073 return TRUE; 1074 } 1075