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_CAN_WRITE_RETRY 24 { 25 FirstTry = 0, 26 RetryAllowRemote = 253, 27 RetryForceCheckPerFile = 254, 28 RetryMasterLocked = 255, 29 } CC_CAN_WRITE_RETRY; 30 31 ULONG CcRosTraceLevel = CC_API_DEBUG; 32 ULONG CcFastMdlReadWait; 33 ULONG CcFastMdlReadNotPossible; 34 ULONG CcFastReadNotPossible; 35 ULONG CcFastReadWait; 36 ULONG CcFastReadNoWait; 37 ULONG CcFastReadResourceMiss; 38 39 /* Counters: 40 * - Amount of pages flushed to the disk 41 * - Number of flush operations 42 */ 43 ULONG CcDataPages = 0; 44 ULONG CcDataFlushes = 0; 45 46 /* FUNCTIONS *****************************************************************/ 47 48 VOID 49 NTAPI 50 MiZeroPhysicalPage ( 51 IN PFN_NUMBER PageFrameIndex 52 ); 53 54 VOID 55 NTAPI 56 CcInitCacheZeroPage ( 57 VOID) 58 { 59 NTSTATUS Status; 60 61 MI_SET_USAGE(MI_USAGE_CACHE); 62 //MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName); 63 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage); 64 if (!NT_SUCCESS(Status)) 65 { 66 DbgPrint("Can't allocate CcZeroPage.\n"); 67 KeBugCheck(CACHE_MANAGER); 68 } 69 MiZeroPhysicalPage(CcZeroPage); 70 } 71 72 VOID 73 CcPostDeferredWrites(VOID) 74 { 75 LIST_ENTRY ToInsertBack; 76 77 InitializeListHead(&ToInsertBack); 78 79 /* We'll try to write as much as we can */ 80 while (TRUE) 81 { 82 PDEFERRED_WRITE DeferredWrite; 83 PLIST_ENTRY ListEntry; 84 85 DeferredWrite = NULL; 86 87 ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock); 88 89 if (!ListEntry) 90 break; 91 92 DeferredWrite = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks); 93 94 /* Check if we can write */ 95 if (CcCanIWrite(DeferredWrite->FileObject, DeferredWrite->BytesToWrite, FALSE, RetryForceCheckPerFile)) 96 { 97 /* If we have an event, set it and go along */ 98 if (DeferredWrite->Event) 99 { 100 KeSetEvent(DeferredWrite->Event, IO_NO_INCREMENT, FALSE); 101 } 102 /* Otherwise, call the write routine and free the context */ 103 else 104 { 105 DeferredWrite->PostRoutine(DeferredWrite->Context1, DeferredWrite->Context2); 106 ExFreePoolWithTag(DeferredWrite, 'CcDw'); 107 } 108 continue; 109 } 110 111 /* Keep it for later */ 112 InsertHeadList(&ToInsertBack, &DeferredWrite->DeferredWriteLinks); 113 114 /* If we don't accept modified pages, stop here */ 115 if (!DeferredWrite->LimitModifiedPages) 116 { 117 break; 118 } 119 } 120 121 /* Insert what we couldn't write back in the list */ 122 while (!IsListEmpty(&ToInsertBack)) 123 { 124 PLIST_ENTRY ListEntry = RemoveTailList(&ToInsertBack); 125 ExInterlockedInsertHeadList(&CcDeferredWrites, ListEntry, &CcDeferredWriteSpinLock); 126 } 127 } 128 129 VOID 130 CcPerformReadAhead( 131 IN PFILE_OBJECT FileObject) 132 { 133 NTSTATUS Status; 134 LONGLONG CurrentOffset; 135 KIRQL OldIrql; 136 PROS_SHARED_CACHE_MAP SharedCacheMap; 137 PROS_VACB Vacb; 138 ULONG PartialLength; 139 ULONG Length; 140 PPRIVATE_CACHE_MAP PrivateCacheMap; 141 BOOLEAN Locked; 142 BOOLEAN Success; 143 144 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 145 146 /* Critical: 147 * PrivateCacheMap might disappear in-between if the handle 148 * to the file is closed (private is attached to the handle not to 149 * the file), so we need to lock the master lock while we deal with 150 * it. It won't disappear without attempting to lock such lock. 151 */ 152 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 153 PrivateCacheMap = FileObject->PrivateCacheMap; 154 /* If the handle was closed since the read ahead was scheduled, just quit */ 155 if (PrivateCacheMap == NULL) 156 { 157 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 158 ObDereferenceObject(FileObject); 159 return; 160 } 161 /* Otherwise, extract read offset and length and release private map */ 162 else 163 { 164 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 165 CurrentOffset = PrivateCacheMap->ReadAheadOffset[1].QuadPart; 166 Length = PrivateCacheMap->ReadAheadLength[1]; 167 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 168 } 169 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 170 171 /* Time to go! */ 172 DPRINT("Doing ReadAhead for %p\n", FileObject); 173 /* Lock the file, first */ 174 if (!SharedCacheMap->Callbacks->AcquireForReadAhead(SharedCacheMap->LazyWriteContext, FALSE)) 175 { 176 Locked = FALSE; 177 goto Clear; 178 } 179 180 /* Remember it's locked */ 181 Locked = TRUE; 182 183 /* Don't read past the end of the file */ 184 if (CurrentOffset >= SharedCacheMap->FileSize.QuadPart) 185 { 186 goto Clear; 187 } 188 if (CurrentOffset + Length > SharedCacheMap->FileSize.QuadPart) 189 { 190 Length = SharedCacheMap->FileSize.QuadPart - CurrentOffset; 191 } 192 193 /* Next of the algorithm will lock like CcCopyData with the slight 194 * difference that we don't copy data back to an user-backed buffer 195 * We just bring data into Cc 196 */ 197 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY; 198 if (PartialLength != 0) 199 { 200 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength); 201 Status = CcRosRequestVacb(SharedCacheMap, 202 ROUND_DOWN(CurrentOffset, VACB_MAPPING_GRANULARITY), 203 &Vacb); 204 if (!NT_SUCCESS(Status)) 205 { 206 DPRINT1("Failed to request VACB: %lx!\n", Status); 207 goto Clear; 208 } 209 210 _SEH2_TRY 211 { 212 Success = CcRosEnsureVacbResident(Vacb, TRUE, FALSE, 213 CurrentOffset % VACB_MAPPING_GRANULARITY, PartialLength); 214 } 215 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 216 { 217 Success = FALSE; 218 } 219 _SEH2_END 220 221 if (!Success) 222 { 223 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE); 224 DPRINT1("Failed to read data: %lx!\n", Status); 225 goto Clear; 226 } 227 228 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE); 229 230 Length -= PartialLength; 231 CurrentOffset += PartialLength; 232 } 233 234 while (Length > 0) 235 { 236 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0); 237 PartialLength = min(VACB_MAPPING_GRANULARITY, Length); 238 Status = CcRosRequestVacb(SharedCacheMap, 239 CurrentOffset, 240 &Vacb); 241 if (!NT_SUCCESS(Status)) 242 { 243 DPRINT1("Failed to request VACB: %lx!\n", Status); 244 goto Clear; 245 } 246 247 _SEH2_TRY 248 { 249 Success = CcRosEnsureVacbResident(Vacb, TRUE, FALSE, 0, PartialLength); 250 } 251 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 252 { 253 Success = FALSE; 254 } 255 _SEH2_END 256 257 if (!Success) 258 { 259 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE); 260 DPRINT1("Failed to read data: %lx!\n", Status); 261 goto Clear; 262 } 263 264 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE); 265 266 Length -= PartialLength; 267 CurrentOffset += PartialLength; 268 } 269 270 Clear: 271 /* See previous comment about private cache map */ 272 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 273 PrivateCacheMap = FileObject->PrivateCacheMap; 274 if (PrivateCacheMap != NULL) 275 { 276 /* Mark read ahead as unactive */ 277 KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 278 InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE); 279 KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock); 280 } 281 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 282 283 /* If file was locked, release it */ 284 if (Locked) 285 { 286 SharedCacheMap->Callbacks->ReleaseFromReadAhead(SharedCacheMap->LazyWriteContext); 287 } 288 289 /* And drop our extra reference (See: CcScheduleReadAhead) */ 290 ObDereferenceObject(FileObject); 291 292 return; 293 } 294 295 /* 296 * @unimplemented 297 */ 298 BOOLEAN 299 NTAPI 300 CcCanIWrite ( 301 IN PFILE_OBJECT FileObject, 302 IN ULONG BytesToWrite, 303 IN BOOLEAN Wait, 304 IN BOOLEAN Retrying) 305 { 306 KIRQL OldIrql; 307 KEVENT WaitEvent; 308 ULONG Length, Pages; 309 BOOLEAN PerFileDefer; 310 DEFERRED_WRITE Context; 311 PFSRTL_COMMON_FCB_HEADER Fcb; 312 CC_CAN_WRITE_RETRY TryContext; 313 PROS_SHARED_CACHE_MAP SharedCacheMap; 314 315 CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n", 316 FileObject, BytesToWrite, Wait, Retrying); 317 318 /* Write through is always OK */ 319 if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH)) 320 { 321 return TRUE; 322 } 323 324 TryContext = Retrying; 325 /* Allow remote file if not from posted */ 326 if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote) 327 { 328 return TRUE; 329 } 330 331 /* Don't exceed max tolerated size */ 332 Length = MAX_ZERO_LENGTH; 333 if (BytesToWrite < MAX_ZERO_LENGTH) 334 { 335 Length = BytesToWrite; 336 } 337 338 Pages = BYTES_TO_PAGES(Length); 339 340 /* By default, assume limits per file won't be hit */ 341 PerFileDefer = FALSE; 342 Fcb = FileObject->FsContext; 343 /* Do we have to check for limits per file? */ 344 if (TryContext >= RetryForceCheckPerFile || 345 BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES)) 346 { 347 /* If master is not locked, lock it now */ 348 if (TryContext != RetryMasterLocked) 349 { 350 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 351 } 352 353 /* Let's not assume the file is cached... */ 354 if (FileObject->SectionObjectPointer != NULL && 355 FileObject->SectionObjectPointer->SharedCacheMap != NULL) 356 { 357 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 358 /* Do we have limits per file set? */ 359 if (SharedCacheMap->DirtyPageThreshold != 0 && 360 SharedCacheMap->DirtyPages != 0) 361 { 362 /* Yes, check whether they are blocking */ 363 if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold) 364 { 365 PerFileDefer = TRUE; 366 } 367 } 368 } 369 370 /* And don't forget to release master */ 371 if (TryContext != RetryMasterLocked) 372 { 373 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 374 } 375 } 376 377 /* So, now allow write if: 378 * - Not the first try or we have no throttling yet 379 * AND: 380 * - We don't exceed threshold! 381 * - We don't exceed what Mm can allow us to use 382 * + If we're above top, that's fine 383 * + If we're above bottom with limited modified pages, that's fine 384 * + Otherwise, throttle! 385 */ 386 if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) && 387 CcTotalDirtyPages + Pages < CcDirtyPageThreshold && 388 (MmAvailablePages > MmThrottleTop || 389 (MmModifiedPageListHead.Total < 1000 && MmAvailablePages > MmThrottleBottom)) && 390 !PerFileDefer) 391 { 392 return TRUE; 393 } 394 395 /* If we can wait, we'll start the wait loop for waiting till we can 396 * write for real 397 */ 398 if (!Wait) 399 { 400 return FALSE; 401 } 402 403 /* Otherwise, if there are no deferred writes yet, start the lazy writer */ 404 if (IsListEmpty(&CcDeferredWrites)) 405 { 406 KIRQL OldIrql; 407 408 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 409 CcScheduleLazyWriteScan(TRUE); 410 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 411 } 412 413 /* Initialize our wait event */ 414 KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE); 415 416 /* And prepare a dummy context */ 417 Context.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 418 Context.NodeByteSize = sizeof(DEFERRED_WRITE); 419 Context.FileObject = FileObject; 420 Context.BytesToWrite = BytesToWrite; 421 Context.LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 422 Context.Event = &WaitEvent; 423 424 /* And queue it */ 425 if (Retrying) 426 { 427 /* To the top, if that's a retry */ 428 ExInterlockedInsertHeadList(&CcDeferredWrites, 429 &Context.DeferredWriteLinks, 430 &CcDeferredWriteSpinLock); 431 } 432 else 433 { 434 /* To the bottom, if that's a first time */ 435 ExInterlockedInsertTailList(&CcDeferredWrites, 436 &Context.DeferredWriteLinks, 437 &CcDeferredWriteSpinLock); 438 } 439 440 #if DBG 441 DPRINT1("Actively deferring write for: %p\n", FileObject); 442 DPRINT1("Because:\n"); 443 if (CcTotalDirtyPages + Pages >= CcDirtyPageThreshold) 444 DPRINT1(" There are too many cache dirty pages: %x + %x >= %x\n", CcTotalDirtyPages, Pages, CcDirtyPageThreshold); 445 if (MmAvailablePages <= MmThrottleTop) 446 DPRINT1(" Available pages are below throttle top: %lx <= %lx\n", MmAvailablePages, MmThrottleTop); 447 if (MmModifiedPageListHead.Total >= 1000) 448 DPRINT1(" There are too many modified pages: %lu >= 1000\n", MmModifiedPageListHead.Total); 449 if (MmAvailablePages <= MmThrottleBottom) 450 DPRINT1(" Available pages are below throttle bottom: %lx <= %lx\n", MmAvailablePages, MmThrottleBottom); 451 #endif 452 /* Now, we'll loop until our event is set. When it is set, it means that caller 453 * can immediately write, and has to 454 */ 455 do 456 { 457 CcPostDeferredWrites(); 458 } while (KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &CcIdleDelay) != STATUS_SUCCESS); 459 460 return TRUE; 461 } 462 463 static 464 int 465 CcpCheckInvalidUserBuffer(PEXCEPTION_POINTERS Except, PVOID Buffer, ULONG Length) 466 { 467 ULONG_PTR ExceptionAddress; 468 ULONG_PTR BeginAddress = (ULONG_PTR)Buffer; 469 ULONG_PTR EndAddress = (ULONG_PTR)Buffer + Length; 470 471 if (Except->ExceptionRecord->ExceptionCode != STATUS_ACCESS_VIOLATION) 472 return EXCEPTION_CONTINUE_SEARCH; 473 if (Except->ExceptionRecord->NumberParameters < 2) 474 return EXCEPTION_CONTINUE_SEARCH; 475 476 ExceptionAddress = Except->ExceptionRecord->ExceptionInformation[1]; 477 if ((ExceptionAddress >= BeginAddress) && (ExceptionAddress < EndAddress)) 478 return EXCEPTION_EXECUTE_HANDLER; 479 480 return EXCEPTION_CONTINUE_SEARCH; 481 } 482 483 /* 484 * @implemented 485 */ 486 BOOLEAN 487 NTAPI 488 CcCopyRead ( 489 IN PFILE_OBJECT FileObject, 490 IN PLARGE_INTEGER FileOffset, 491 IN ULONG Length, 492 IN BOOLEAN Wait, 493 OUT PVOID Buffer, 494 OUT PIO_STATUS_BLOCK IoStatus) 495 { 496 PROS_VACB Vacb; 497 PROS_SHARED_CACHE_MAP SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 498 NTSTATUS Status; 499 LONGLONG CurrentOffset; 500 LONGLONG ReadEnd = FileOffset->QuadPart + Length; 501 ULONG ReadLength = 0; 502 503 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n", 504 FileObject, FileOffset->QuadPart, Length, Wait); 505 506 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, " 507 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n", 508 FileObject, FileOffset->QuadPart, Length, Wait, 509 Buffer, IoStatus); 510 511 if (!SharedCacheMap) 512 return FALSE; 513 514 /* Documented to ASSERT, but KMTests test this case... */ 515 // ASSERT((FileOffset->QuadPart + Length) <= SharedCacheMap->FileSize.QuadPart); 516 517 CurrentOffset = FileOffset->QuadPart; 518 while(CurrentOffset < ReadEnd) 519 { 520 Status = CcRosGetVacb(SharedCacheMap, CurrentOffset, &Vacb); 521 if (!NT_SUCCESS(Status)) 522 { 523 ExRaiseStatus(Status); 524 return FALSE; 525 } 526 527 _SEH2_TRY 528 { 529 ULONG VacbOffset = CurrentOffset % VACB_MAPPING_GRANULARITY; 530 ULONG VacbLength = min(Length, VACB_MAPPING_GRANULARITY - VacbOffset); 531 SIZE_T CopyLength = VacbLength; 532 533 if (!CcRosEnsureVacbResident(Vacb, Wait, FALSE, VacbOffset, VacbLength)) 534 return FALSE; 535 536 _SEH2_TRY 537 { 538 RtlCopyMemory(Buffer, (PUCHAR)Vacb->BaseAddress + VacbOffset, CopyLength); 539 } 540 _SEH2_EXCEPT(CcpCheckInvalidUserBuffer(_SEH2_GetExceptionInformation(), Buffer, VacbLength)) 541 { 542 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 543 } 544 _SEH2_END; 545 546 ReadLength += VacbLength; 547 548 Buffer = (PVOID)((ULONG_PTR)Buffer + VacbLength); 549 CurrentOffset += VacbLength; 550 Length -= VacbLength; 551 } 552 _SEH2_FINALLY 553 { 554 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE); 555 } 556 _SEH2_END; 557 } 558 559 IoStatus->Status = STATUS_SUCCESS; 560 IoStatus->Information = ReadLength; 561 562 #if 0 563 /* If that was a successful sync read operation, let's handle read ahead */ 564 if (Length == 0 && Wait) 565 { 566 PPRIVATE_CACHE_MAP PrivateCacheMap = FileObject->PrivateCacheMap; 567 568 /* If file isn't random access and next read may get us cross VACB boundary, 569 * schedule next read 570 */ 571 if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS) && 572 (CurrentOffset - 1) / VACB_MAPPING_GRANULARITY != (CurrentOffset + ReadLength - 1) / VACB_MAPPING_GRANULARITY) 573 { 574 CcScheduleReadAhead(FileObject, FileOffset, ReadLength); 575 } 576 577 /* And update read history in private cache map */ 578 PrivateCacheMap->FileOffset1.QuadPart = PrivateCacheMap->FileOffset2.QuadPart; 579 PrivateCacheMap->BeyondLastByte1.QuadPart = PrivateCacheMap->BeyondLastByte2.QuadPart; 580 PrivateCacheMap->FileOffset2.QuadPart = FileOffset->QuadPart; 581 PrivateCacheMap->BeyondLastByte2.QuadPart = FileOffset->QuadPart + ReadLength; 582 } 583 #endif 584 585 return TRUE; 586 } 587 588 /* 589 * @implemented 590 */ 591 BOOLEAN 592 NTAPI 593 CcCopyWrite ( 594 IN PFILE_OBJECT FileObject, 595 IN PLARGE_INTEGER FileOffset, 596 IN ULONG Length, 597 IN BOOLEAN Wait, 598 IN PVOID Buffer) 599 { 600 PROS_VACB Vacb; 601 PROS_SHARED_CACHE_MAP SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 602 NTSTATUS Status; 603 LONGLONG CurrentOffset; 604 LONGLONG WriteEnd; 605 606 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n", 607 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 608 609 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, " 610 "Length %lu, Wait %u, Buffer 0x%p)\n", 611 FileObject, FileOffset->QuadPart, Length, Wait, Buffer); 612 613 if (!SharedCacheMap) 614 return FALSE; 615 616 Status = RtlLongLongAdd(FileOffset->QuadPart, Length, &WriteEnd); 617 if (!NT_SUCCESS(Status)) 618 ExRaiseStatus(Status); 619 620 ASSERT(WriteEnd <= SharedCacheMap->SectionSize.QuadPart); 621 622 CurrentOffset = FileOffset->QuadPart; 623 while(CurrentOffset < WriteEnd) 624 { 625 ULONG VacbOffset = CurrentOffset % VACB_MAPPING_GRANULARITY; 626 ULONG VacbLength = min(WriteEnd - CurrentOffset, VACB_MAPPING_GRANULARITY - VacbOffset); 627 628 Status = CcRosGetVacb(SharedCacheMap, CurrentOffset, &Vacb); 629 if (!NT_SUCCESS(Status)) 630 { 631 ExRaiseStatus(Status); 632 return FALSE; 633 } 634 635 _SEH2_TRY 636 { 637 if (!CcRosEnsureVacbResident(Vacb, Wait, FALSE, VacbOffset, VacbLength)) 638 { 639 return FALSE; 640 } 641 642 _SEH2_TRY 643 { 644 RtlCopyMemory((PVOID)((ULONG_PTR)Vacb->BaseAddress + VacbOffset), Buffer, VacbLength); 645 } 646 _SEH2_EXCEPT(CcpCheckInvalidUserBuffer(_SEH2_GetExceptionInformation(), Buffer, VacbLength)) 647 { 648 ExRaiseStatus(STATUS_INVALID_USER_BUFFER); 649 } 650 _SEH2_END; 651 652 Buffer = (PVOID)((ULONG_PTR)Buffer + VacbLength); 653 CurrentOffset += VacbLength; 654 655 /* Tell Mm */ 656 Status = MmMakeSegmentDirty(FileObject->SectionObjectPointer, 657 Vacb->FileOffset.QuadPart + VacbOffset, 658 VacbLength); 659 if (!NT_SUCCESS(Status)) 660 ExRaiseStatus(Status); 661 } 662 _SEH2_FINALLY 663 { 664 /* Do not mark the VACB as dirty if an exception was raised */ 665 CcRosReleaseVacb(SharedCacheMap, Vacb, !_SEH2_AbnormalTermination(), FALSE); 666 } 667 _SEH2_END; 668 } 669 670 /* Flush if needed */ 671 if (FileObject->Flags & FO_WRITE_THROUGH) 672 CcFlushCache(FileObject->SectionObjectPointer, FileOffset, Length, NULL); 673 674 return TRUE; 675 } 676 677 /* 678 * @implemented 679 */ 680 VOID 681 NTAPI 682 CcDeferWrite ( 683 IN PFILE_OBJECT FileObject, 684 IN PCC_POST_DEFERRED_WRITE PostRoutine, 685 IN PVOID Context1, 686 IN PVOID Context2, 687 IN ULONG BytesToWrite, 688 IN BOOLEAN Retrying) 689 { 690 KIRQL OldIrql; 691 PDEFERRED_WRITE Context; 692 PFSRTL_COMMON_FCB_HEADER Fcb; 693 694 CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n", 695 FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying); 696 697 /* Try to allocate a context for queueing the write operation */ 698 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEFERRED_WRITE), 'CcDw'); 699 /* If it failed, immediately execute the operation! */ 700 if (Context == NULL) 701 { 702 PostRoutine(Context1, Context2); 703 return; 704 } 705 706 Fcb = FileObject->FsContext; 707 708 /* Otherwise, initialize the context */ 709 RtlZeroMemory(Context, sizeof(DEFERRED_WRITE)); 710 Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE; 711 Context->NodeByteSize = sizeof(DEFERRED_WRITE); 712 Context->FileObject = FileObject; 713 Context->PostRoutine = PostRoutine; 714 Context->Context1 = Context1; 715 Context->Context2 = Context2; 716 Context->BytesToWrite = BytesToWrite; 717 Context->LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES); 718 719 /* And queue it */ 720 if (Retrying) 721 { 722 /* To the top, if that's a retry */ 723 ExInterlockedInsertHeadList(&CcDeferredWrites, 724 &Context->DeferredWriteLinks, 725 &CcDeferredWriteSpinLock); 726 } 727 else 728 { 729 /* To the bottom, if that's a first time */ 730 ExInterlockedInsertTailList(&CcDeferredWrites, 731 &Context->DeferredWriteLinks, 732 &CcDeferredWriteSpinLock); 733 } 734 735 /* Try to execute the posted writes */ 736 CcPostDeferredWrites(); 737 738 /* Schedule a lazy writer run to handle deferred writes */ 739 OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); 740 if (!LazyWriter.ScanActive) 741 { 742 CcScheduleLazyWriteScan(FALSE); 743 } 744 KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); 745 } 746 747 /* 748 * @unimplemented 749 */ 750 VOID 751 NTAPI 752 CcFastCopyRead ( 753 IN PFILE_OBJECT FileObject, 754 IN ULONG FileOffset, 755 IN ULONG Length, 756 IN ULONG PageCount, 757 OUT PVOID Buffer, 758 OUT PIO_STATUS_BLOCK IoStatus) 759 { 760 LARGE_INTEGER LargeFileOffset; 761 BOOLEAN Success; 762 763 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n", 764 FileObject, FileOffset, Length, PageCount, Buffer); 765 766 DBG_UNREFERENCED_PARAMETER(PageCount); 767 768 LargeFileOffset.QuadPart = FileOffset; 769 Success = CcCopyRead(FileObject, 770 &LargeFileOffset, 771 Length, 772 TRUE, 773 Buffer, 774 IoStatus); 775 ASSERT(Success == TRUE); 776 } 777 778 /* 779 * @unimplemented 780 */ 781 VOID 782 NTAPI 783 CcFastCopyWrite ( 784 IN PFILE_OBJECT FileObject, 785 IN ULONG FileOffset, 786 IN ULONG Length, 787 IN PVOID Buffer) 788 { 789 LARGE_INTEGER LargeFileOffset; 790 BOOLEAN Success; 791 792 CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n", 793 FileObject, FileOffset, Length, Buffer); 794 795 LargeFileOffset.QuadPart = FileOffset; 796 Success = CcCopyWrite(FileObject, 797 &LargeFileOffset, 798 Length, 799 TRUE, 800 Buffer); 801 ASSERT(Success == TRUE); 802 } 803 804 /* 805 * @implemented 806 */ 807 BOOLEAN 808 NTAPI 809 CcZeroData ( 810 IN PFILE_OBJECT FileObject, 811 IN PLARGE_INTEGER StartOffset, 812 IN PLARGE_INTEGER EndOffset, 813 IN BOOLEAN Wait) 814 { 815 NTSTATUS Status; 816 LARGE_INTEGER WriteOffset; 817 LONGLONG Length; 818 PROS_VACB Vacb; 819 PROS_SHARED_CACHE_MAP SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap; 820 821 CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n", 822 FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait); 823 824 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, " 825 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart, 826 Wait); 827 828 Length = EndOffset->QuadPart - StartOffset->QuadPart; 829 WriteOffset.QuadPart = StartOffset->QuadPart; 830 831 if (!SharedCacheMap) 832 { 833 /* Make this a non-cached write */ 834 IO_STATUS_BLOCK Iosb; 835 KEVENT Event; 836 PMDL Mdl; 837 ULONG i; 838 ULONG CurrentLength; 839 PPFN_NUMBER PfnArray; 840 841 /* Setup our Mdl */ 842 Mdl = IoAllocateMdl(NULL, min(Length, MAX_ZERO_LENGTH), FALSE, FALSE, NULL); 843 if (!Mdl) 844 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 845 846 PfnArray = MmGetMdlPfnArray(Mdl); 847 for (i = 0; i < BYTES_TO_PAGES(Mdl->ByteCount); i++) 848 PfnArray[i] = CcZeroPage; 849 Mdl->MdlFlags |= MDL_PAGES_LOCKED; 850 851 /* Perform the write sequencially */ 852 while (Length > 0) 853 { 854 CurrentLength = min(Length, MAX_ZERO_LENGTH); 855 856 Mdl->ByteCount = CurrentLength; 857 858 KeInitializeEvent(&Event, NotificationEvent, FALSE); 859 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb); 860 if (Status == STATUS_PENDING) 861 { 862 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 863 Status = Iosb.Status; 864 } 865 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) 866 { 867 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); 868 } 869 if (!NT_SUCCESS(Status)) 870 { 871 IoFreeMdl(Mdl); 872 ExRaiseStatus(Status); 873 } 874 WriteOffset.QuadPart += CurrentLength; 875 Length -= CurrentLength; 876 } 877 878 IoFreeMdl(Mdl); 879 880 return TRUE; 881 } 882 883 /* See if we should simply truncate the valid data length */ 884 if ((StartOffset->QuadPart < SharedCacheMap->ValidDataLength.QuadPart) && (EndOffset->QuadPart >= SharedCacheMap->ValidDataLength.QuadPart)) 885 { 886 DPRINT1("Truncating VDL.\n"); 887 SharedCacheMap->ValidDataLength = *StartOffset; 888 return TRUE; 889 } 890 891 ASSERT(EndOffset->QuadPart <= SharedCacheMap->SectionSize.QuadPart); 892 893 while(WriteOffset.QuadPart < EndOffset->QuadPart) 894 { 895 ULONG VacbOffset = WriteOffset.QuadPart % VACB_MAPPING_GRANULARITY; 896 ULONG VacbLength = min(Length, VACB_MAPPING_GRANULARITY - VacbOffset); 897 898 Status = CcRosGetVacb(SharedCacheMap, WriteOffset.QuadPart, &Vacb); 899 if (!NT_SUCCESS(Status)) 900 { 901 ExRaiseStatus(Status); 902 return FALSE; 903 } 904 905 _SEH2_TRY 906 { 907 if (!CcRosEnsureVacbResident(Vacb, Wait, FALSE, VacbOffset, VacbLength)) 908 { 909 return FALSE; 910 } 911 912 RtlZeroMemory((PVOID)((ULONG_PTR)Vacb->BaseAddress + VacbOffset), VacbLength); 913 914 WriteOffset.QuadPart += VacbLength; 915 Length -= VacbLength; 916 917 /* Tell Mm */ 918 Status = MmMakeSegmentDirty(FileObject->SectionObjectPointer, 919 Vacb->FileOffset.QuadPart + VacbOffset, 920 VacbLength); 921 if (!NT_SUCCESS(Status)) 922 ExRaiseStatus(Status); 923 } 924 _SEH2_FINALLY 925 { 926 /* Do not mark the VACB as dirty if an exception was raised */ 927 CcRosReleaseVacb(SharedCacheMap, Vacb, !_SEH2_AbnormalTermination(), FALSE); 928 } 929 _SEH2_END; 930 } 931 932 /* Flush if needed */ 933 if (FileObject->Flags & FO_WRITE_THROUGH) 934 CcFlushCache(FileObject->SectionObjectPointer, StartOffset, EndOffset->QuadPart - StartOffset->QuadPart, NULL); 935 936 return TRUE; 937 } 938