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