[NTOS:CC] Don't read past the end of the file in CcPerformReadAhead.
[reactos.git] / ntoskrnl / cc / copy.c
index 8f95814..496cef6 100644 (file)
@@ -4,7 +4,8 @@
  * FILE:            ntoskrnl/cc/copy.c
  * PURPOSE:         Implements cache managers copy interface
  *
- * PROGRAMMERS:
+ * PROGRAMMERS:     Some people?
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES ******************************************************************/
 static PFN_NUMBER CcZeroPage = 0;
 
 #define MAX_ZERO_LENGTH    (256 * 1024)
-#define MAX_RW_LENGTH    (256 * 1024)
 
+typedef enum _CC_COPY_OPERATION
+{
+    CcOperationRead,
+    CcOperationWrite,
+    CcOperationZero
+} CC_COPY_OPERATION;
+
+typedef enum _CC_CAN_WRITE_RETRY
+{
+    FirstTry = 0,
+    RetryAllowRemote = 253,
+    RetryForceCheckPerFile = 254,
+    RetryMasterLocked = 255,
+} CC_CAN_WRITE_RETRY;
+
+ULONG CcRosTraceLevel = 0;
 ULONG CcFastMdlReadWait;
 ULONG CcFastMdlReadNotPossible;
 ULONG CcFastReadNotPossible;
@@ -55,170 +71,53 @@ CcInitCacheZeroPage (
 
 NTSTATUS
 NTAPI
-ReadCacheSegmentChain (
-    PBCB Bcb,
-    ULONG ReadOffset,
-    ULONG Length,
-    PVOID Buffer)
-{
-    PCACHE_SEGMENT head;
-    PCACHE_SEGMENT current;
-    PCACHE_SEGMENT previous;
-    IO_STATUS_BLOCK Iosb;
-    LARGE_INTEGER SegOffset;
-    NTSTATUS Status;
-    ULONG TempLength;
-    KEVENT Event;
-    PMDL Mdl;
-
-    Mdl = _alloca(MmSizeOfMdl(NULL, MAX_RW_LENGTH));
-
-    Status = CcRosGetCacheSegmentChain(Bcb, ReadOffset, Length, &head);
-    if (!NT_SUCCESS(Status))
-    {
-        return Status;
-    }
-    current = head;
-    while (current != NULL)
-    {
-        /*
-         * If the current segment is valid then copy it into the
-         * user buffer.
-         */
-        if (current->Valid)
-        {
-            TempLength = min(Bcb->CacheSegmentSize, Length);
-            memcpy(Buffer, current->BaseAddress, TempLength);
-
-            Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
-
-            Length = Length - TempLength;
-            previous = current;
-            current = current->NextInChain;
-            CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
-        }
-        /*
-         * Otherwise read in as much as we can.
-         */
-        else
-        {
-            PCACHE_SEGMENT current2;
-            ULONG current_size;
-            ULONG i;
-            PPFN_NUMBER MdlPages;
-
-            /*
-             * Count the maximum number of bytes we could read starting
-             * from the current segment.
-             */
-            current2 = current;
-            current_size = 0;
-            while ((current2 != NULL) && !current2->Valid && (current_size < MAX_RW_LENGTH))
-            {
-                current2 = current2->NextInChain;
-                current_size += Bcb->CacheSegmentSize;
-            }
-
-            /*
-             * Create an MDL which contains all their pages.
-             */
-            MmInitializeMdl(Mdl, NULL, current_size);
-            Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
-            current2 = current;
-            current_size = 0;
-            MdlPages = (PPFN_NUMBER)(Mdl + 1);
-            while ((current2 != NULL) && !current2->Valid && (current_size < MAX_RW_LENGTH))
-            {
-                PVOID address = current2->BaseAddress;
-                for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++, address = RVA(address, PAGE_SIZE))
-                {
-                    *MdlPages++ = MmGetPfnForProcess(NULL, address);
-                }
-                current2 = current2->NextInChain;
-                current_size += Bcb->CacheSegmentSize;
-            }
-
-            /*
-             * Read in the information.
-             */
-            SegOffset.QuadPart = current->FileOffset;
-            KeInitializeEvent(&Event, NotificationEvent, FALSE);
-            Status = IoPageRead(Bcb->FileObject,
-                                Mdl,
-                                &SegOffset,
-                                &Event,
-                                &Iosb);
-            if (Status == STATUS_PENDING)
-            {
-                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-                Status = Iosb.Status;
-            }
-            if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
-            {
-                MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
-            }
-            if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
-            {
-                while (current != NULL)
-                {
-                    previous = current;
-                    current = current->NextInChain;
-                    CcRosReleaseCacheSegment(Bcb, previous, FALSE, FALSE, FALSE);
-                }
-                return Status;
-            }
-            current_size = 0;
-            while (current != NULL && !current->Valid && current_size < MAX_RW_LENGTH)
-            {
-                previous = current;
-                current = current->NextInChain;
-                TempLength = min(Bcb->CacheSegmentSize, Length);
-                memcpy(Buffer, previous->BaseAddress, TempLength);
-
-                Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
-
-                Length = Length - TempLength;
-                CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
-                current_size += Bcb->CacheSegmentSize;
-            }
-        }
-    }
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS
-NTAPI
-ReadCacheSegment (
-    PCACHE_SEGMENT CacheSeg)
+CcReadVirtualAddress (
+    PROS_VACB Vacb)
 {
-    ULONG Size;
+    ULONG Size, Pages;
     PMDL Mdl;
     NTSTATUS Status;
-    LARGE_INTEGER SegOffset;
     IO_STATUS_BLOCK IoStatus;
     KEVENT Event;
 
-    SegOffset.QuadPart = CacheSeg->FileOffset;
-    Size = (ULONG)(CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset);
-    if (Size > CacheSeg->Bcb->CacheSegmentSize)
+    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
+    if (Size > VACB_MAPPING_GRANULARITY)
     {
-        Size = CacheSeg->Bcb->CacheSegmentSize;
+        Size = VACB_MAPPING_GRANULARITY;
     }
 
-    Mdl = IoAllocateMdl(CacheSeg->BaseAddress, Size, FALSE, FALSE, NULL);
+    Pages = BYTES_TO_PAGES(Size);
+    ASSERT(Pages * PAGE_SIZE <= VACB_MAPPING_GRANULARITY);
+
+    Mdl = IoAllocateMdl(Vacb->BaseAddress, Pages * PAGE_SIZE, FALSE, FALSE, NULL);
     if (!Mdl)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    MmBuildMdlForNonPagedPool(Mdl);
-    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Status = IoPageRead(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(Status))
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        Mdl->MdlFlags |= MDL_IO_PAGE_READ;
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatus.Status;
+        }
+
+        MmUnlockPages(Mdl);
     }
 
     IoFreeMdl(Mdl);
@@ -229,10 +128,10 @@ ReadCacheSegment (
         return Status;
     }
 
-    if (CacheSeg->Bcb->CacheSegmentSize > Size)
+    if (Size < VACB_MAPPING_GRANULARITY)
     {
-        RtlZeroMemory((char*)CacheSeg->BaseAddress + Size,
-                      CacheSeg->Bcb->CacheSegmentSize - Size);
+        RtlZeroMemory((char*)Vacb->BaseAddress + Size,
+                      VACB_MAPPING_GRANULARITY - Size);
     }
 
     return STATUS_SUCCESS;
@@ -240,22 +139,19 @@ ReadCacheSegment (
 
 NTSTATUS
 NTAPI
-WriteCacheSegment (
-    PCACHE_SEGMENT CacheSeg)
+CcWriteVirtualAddress (
+    PROS_VACB Vacb)
 {
     ULONG Size;
     PMDL Mdl;
     NTSTATUS Status;
     IO_STATUS_BLOCK IoStatus;
-    LARGE_INTEGER SegOffset;
     KEVENT Event;
 
-    CacheSeg->Dirty = FALSE;
-    SegOffset.QuadPart = CacheSeg->FileOffset;
-    Size = (ULONG)(CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset);
-    if (Size > CacheSeg->Bcb->CacheSegmentSize)
+    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
+    if (Size > VACB_MAPPING_GRANULARITY)
     {
-        Size = CacheSeg->Bcb->CacheSegmentSize;
+        Size = VACB_MAPPING_GRANULARITY;
     }
     //
     // Nonpaged pool PDEs in ReactOS must actually be synchronized between the
@@ -265,291 +161,685 @@ WriteCacheSegment (
         ULONG i = 0;
         do
         {
-            MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)CacheSeg->BaseAddress + (i << PAGE_SHIFT)));
+            MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i << PAGE_SHIFT)));
         } while (++i < (Size >> PAGE_SHIFT));
     }
 
-    Mdl = IoAllocateMdl(CacheSeg->BaseAddress, Size, FALSE, FALSE, NULL);
+    Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
     if (!Mdl)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    MmBuildMdlForNonPagedPool(Mdl);
-    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Status = IoSynchronousPageWrite(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(Status))
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatus.Status;
+        }
+
+        MmUnlockPages(Mdl);
     }
     IoFreeMdl(Mdl);
     if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
     {
         DPRINT1("IoPageWrite failed, Status %x\n", Status);
-        CacheSeg->Dirty = TRUE;
         return Status;
     }
 
     return STATUS_SUCCESS;
 }
 
-
-/*
- * @unimplemented
- */
-BOOLEAN
-NTAPI
-CcCanIWrite (
-    IN PFILE_OBJECT FileObject,
-    IN ULONG BytesToWrite,
-    IN BOOLEAN Wait,
-    IN BOOLEAN Retrying)
+NTSTATUS
+ReadWriteOrZero(
+    _Inout_ PVOID BaseAddress,
+    _Inout_opt_ PVOID Buffer,
+    _In_ ULONG Length,
+    _In_ CC_COPY_OPERATION Operation)
 {
-    UNIMPLEMENTED;
-    return FALSE;
-}
+    NTSTATUS Status = STATUS_SUCCESS;
 
+    if (Operation == CcOperationZero)
+    {
+        /* Zero */
+        RtlZeroMemory(BaseAddress, Length);
+    }
+    else
+    {
+        _SEH2_TRY
+        {
+            if (Operation == CcOperationWrite)
+                RtlCopyMemory(BaseAddress, Buffer, Length);
+            else
+                RtlCopyMemory(Buffer, BaseAddress, Length);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+    }
+    return Status;
+}
 
-/*
- * @implemented
- */
 BOOLEAN
-NTAPI
-CcCopyRead (
-    IN PFILE_OBJECT FileObject,
-    IN PLARGE_INTEGER FileOffset,
-    IN ULONG Length,
-    IN BOOLEAN Wait,
-    OUT PVOID Buffer,
-    OUT PIO_STATUS_BLOCK IoStatus)
+CcCopyData (
+    _In_ PFILE_OBJECT FileObject,
+    _In_ LONGLONG FileOffset,
+    _Inout_ PVOID Buffer,
+    _In_ LONGLONG Length,
+    _In_ CC_COPY_OPERATION Operation,
+    _In_ BOOLEAN Wait,
+    _Out_ PIO_STATUS_BLOCK IoStatus)
 {
-    ULONG ReadOffset;
-    ULONG TempLength;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status;
+    LONGLONG CurrentOffset;
+    ULONG BytesCopied;
+    KIRQL OldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+    PLIST_ENTRY ListEntry;
+    PROS_VACB Vacb;
+    ULONG PartialLength;
     PVOID BaseAddress;
-    PCACHE_SEGMENT CacheSeg;
     BOOLEAN Valid;
-    ULONG ReadLength = 0;
-    PBCB Bcb;
-    KIRQL oldirql;
-    PLIST_ENTRY current_entry;
-    PCACHE_SEGMENT current;
-
-    DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
-           "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
-           FileObject, FileOffset->QuadPart, Length, Wait,
-           Buffer, IoStatus);
-
-    Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
-    ReadOffset = (ULONG)FileOffset->QuadPart;
+    PPRIVATE_CACHE_MAP PrivateCacheMap;
 
-    DPRINT("AllocationSize %I64d, FileSize %I64d\n",
-           Bcb->AllocationSize.QuadPart,
-           Bcb->FileSize.QuadPart);
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+    CurrentOffset = FileOffset;
+    BytesCopied = 0;
 
-    /*
-     * Check for the nowait case that all the cache segments that would
-     * cover this read are in memory.
-     */
     if (!Wait)
     {
-        KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
-        current_entry = Bcb->BcbSegmentListHead.Flink;
-        while (current_entry != &Bcb->BcbSegmentListHead)
+        /* test if the requested data is available */
+        KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
+        /* FIXME: this loop doesn't take into account areas that don't have
+         * a VACB in the list yet */
+        ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
+        while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
         {
-            current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
-                                        BcbSegmentListEntry);
-            if (!current->Valid && current->FileOffset < ReadOffset + Length
-                    && current->FileOffset + Bcb->CacheSegmentSize > ReadOffset)
+            Vacb = CONTAINING_RECORD(ListEntry,
+                                     ROS_VACB,
+                                     CacheMapVacbListEntry);
+            ListEntry = ListEntry->Flink;
+            if (!Vacb->Valid &&
+                DoRangesIntersect(Vacb->FileOffset.QuadPart,
+                                  VACB_MAPPING_GRANULARITY,
+                                  CurrentOffset, Length))
             {
-                KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-                IoStatus->Status = STATUS_UNSUCCESSFUL;
-                IoStatus->Information = 0;
+                KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
+                /* data not available */
                 return FALSE;
             }
-            current_entry = current_entry->Flink;
+            if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length)
+                break;
         }
-        KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
+        KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
     }
 
-    TempLength = ReadOffset % Bcb->CacheSegmentSize;
-    if (TempLength != 0)
+    PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
+    if (PartialLength != 0)
     {
-        TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
-        Status = CcRosRequestCacheSegment(Bcb,
-                                          ROUND_DOWN(ReadOffset,
-                                                  Bcb->CacheSegmentSize),
-                                          &BaseAddress, &Valid, &CacheSeg);
+        PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
+        Status = CcRosRequestVacb(SharedCacheMap,
+                                  ROUND_DOWN(CurrentOffset,
+                                             VACB_MAPPING_GRANULARITY),
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
         if (!NT_SUCCESS(Status))
-        {
-            IoStatus->Information = 0;
-            IoStatus->Status = Status;
-            DPRINT("CcRosRequestCacheSegment faild, Status %x\n", Status);
-            return FALSE;
-        }
+            ExRaiseStatus(Status);
         if (!Valid)
         {
-            Status = ReadCacheSegment(CacheSeg);
+            Status = CcReadVirtualAddress(Vacb);
             if (!NT_SUCCESS(Status))
             {
-                IoStatus->Information = 0;
-                IoStatus->Status = Status;
-                CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
-                return FALSE;
+                CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+                ExRaiseStatus(Status);
             }
         }
-        memcpy (Buffer, (char*)BaseAddress + ReadOffset % Bcb->CacheSegmentSize,
-                TempLength);
-        CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
-        ReadLength += TempLength;
-        Length -= TempLength;
-        ReadOffset += TempLength;
-        Buffer = (PVOID)((char*)Buffer + TempLength);
+        Status = ReadWriteOrZero((PUCHAR)BaseAddress + CurrentOffset % VACB_MAPPING_GRANULARITY,
+                                 Buffer,
+                                 PartialLength,
+                                 Operation);
+
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
+
+        if (!NT_SUCCESS(Status))
+            ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
+
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
+        BytesCopied += PartialLength;
+
+        if (Operation != CcOperationZero)
+            Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
     }
 
     while (Length > 0)
     {
-        TempLength = min(max(Bcb->CacheSegmentSize, MAX_RW_LENGTH), Length);
-        Status = ReadCacheSegmentChain(Bcb, ReadOffset, TempLength, Buffer);
+        ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
+        PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
+        Status = CcRosRequestVacb(SharedCacheMap,
+                                  CurrentOffset,
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
         if (!NT_SUCCESS(Status))
+            ExRaiseStatus(Status);
+        if (!Valid &&
+            (Operation == CcOperationRead ||
+             PartialLength < VACB_MAPPING_GRANULARITY))
         {
-            IoStatus->Information = 0;
-            IoStatus->Status = Status;
-            DPRINT("ReadCacheSegmentChain failed, Status %x\n", Status);
-            return FALSE;
+            Status = CcReadVirtualAddress(Vacb);
+            if (!NT_SUCCESS(Status))
+            {
+                CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+                ExRaiseStatus(Status);
+            }
         }
+        Status = ReadWriteOrZero(BaseAddress, Buffer, PartialLength, Operation);
+
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
 
-        ReadLength += TempLength;
-        Length -= TempLength;
-        ReadOffset += TempLength;
+        if (!NT_SUCCESS(Status))
+            ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
+
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
+        BytesCopied += PartialLength;
+
+        if (Operation != CcOperationZero)
+            Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
+    }
+
+    /* If that was a successful sync read operation, let's handle read ahead */
+    if (Operation == CcOperationRead && Length == 0 && Wait)
+    {
+        /* If file isn't random access, schedule next read */
+        if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
+        {
+            CcScheduleReadAhead(FileObject, (PLARGE_INTEGER)&FileOffset, BytesCopied);
+        }
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+        /* And update read history in private cache map */
+        PrivateCacheMap->FileOffset1.QuadPart = PrivateCacheMap->FileOffset2.QuadPart;
+        PrivateCacheMap->BeyondLastByte1.QuadPart = PrivateCacheMap->BeyondLastByte2.QuadPart;
+        PrivateCacheMap->FileOffset2.QuadPart = FileOffset;
+        PrivateCacheMap->BeyondLastByte2.QuadPart = FileOffset + BytesCopied;
     }
 
     IoStatus->Status = STATUS_SUCCESS;
-    IoStatus->Information = ReadLength;
-    DPRINT("CcCopyRead O.K.\n");
+    IoStatus->Information = BytesCopied;
     return TRUE;
 }
 
-/*
- * @implemented
- */
-BOOLEAN
-NTAPI
-CcCopyWrite (
-    IN PFILE_OBJECT FileObject,
-    IN PLARGE_INTEGER FileOffset,
-    IN ULONG Length,
-    IN BOOLEAN Wait,
-    IN PVOID Buffer)
+VOID
+CcPostDeferredWrites(VOID)
+{
+    ULONG WrittenBytes;
+
+    /* We'll try to write as much as we can */
+    WrittenBytes = 0;
+    while (TRUE)
+    {
+        KIRQL OldIrql;
+        PLIST_ENTRY ListEntry;
+        PDEFERRED_WRITE DeferredWrite;
+
+        DeferredWrite = NULL;
+
+        /* Lock our deferred writes list */
+        KeAcquireSpinLock(&CcDeferredWriteSpinLock, &OldIrql);
+        for (ListEntry = CcDeferredWrites.Flink;
+             ListEntry != &CcDeferredWrites;
+             ListEntry = ListEntry->Flink)
+        {
+            /* Extract an entry */
+            DeferredWrite = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
+
+            /* Compute the modified bytes, based on what we already wrote */
+            WrittenBytes += DeferredWrite->BytesToWrite;
+            /* We overflowed, give up */
+            if (WrittenBytes < DeferredWrite->BytesToWrite)
+            {
+                DeferredWrite = NULL;
+                break;
+            }
+
+            /* Check we can write */
+            if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, RetryForceCheckPerFile))
+            {
+                /* We can, so remove it from the list and stop looking for entry */
+                RemoveEntryList(&DeferredWrite->DeferredWriteLinks);
+                break;
+            }
+
+            /* If we don't accept modified pages, stop here */
+            if (!DeferredWrite->LimitModifiedPages)
+            {
+                DeferredWrite = NULL;
+                break;
+            }
+
+            /* Reset count as nothing was written yet */
+            WrittenBytes -= DeferredWrite->BytesToWrite;
+            DeferredWrite = NULL;
+        }
+        KeReleaseSpinLock(&CcDeferredWriteSpinLock, OldIrql);
+
+        /* Nothing to write found, give up */
+        if (DeferredWrite == NULL)
+        {
+            break;
+        }
+
+        /* If we have an event, set it and quit */
+        if (DeferredWrite->Event)
+        {
+            KeSetEvent(DeferredWrite->Event, IO_NO_INCREMENT, FALSE);
+        }
+        /* Otherwise, call the write routine and free the context */
+        else
+        {
+            DeferredWrite->PostRoutine(DeferredWrite->Context1, DeferredWrite->Context2);
+            ExFreePoolWithTag(DeferredWrite, 'CcDw');
+        }
+    }
+}
+
+VOID
+CcPerformReadAhead(
+    IN PFILE_OBJECT FileObject)
 {
     NTSTATUS Status;
-    ULONG WriteOffset;
-    KIRQL oldirql;
-    PBCB Bcb;
-    PLIST_ENTRY current_entry;
-    PCACHE_SEGMENT CacheSeg;
-    ULONG TempLength;
+    LONGLONG CurrentOffset;
+    KIRQL OldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+    PROS_VACB Vacb;
+    ULONG PartialLength;
     PVOID BaseAddress;
     BOOLEAN Valid;
+    ULONG Length;
+    PPRIVATE_CACHE_MAP PrivateCacheMap;
+    BOOLEAN Locked;
 
-    DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
-           "Length %lu, Wait %u, Buffer 0x%p)\n",
-           FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
 
-    Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
-    WriteOffset = (ULONG)FileOffset->QuadPart;
+    /* Critical:
+     * PrivateCacheMap might disappear in-between if the handle
+     * to the file is closed (private is attached to the handle not to
+     * the file), so we need to lock the master lock while we deal with
+     * it. It won't disappear without attempting to lock such lock.
+     */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+    /* If the handle was closed since the read ahead was scheduled, just quit */
+    if (PrivateCacheMap == NULL)
+    {
+        KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+        ObDereferenceObject(FileObject);
+        return;
+    }
+    /* Otherwise, extract read offset and length and release private map */
+    else
+    {
+        KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+        CurrentOffset = PrivateCacheMap->ReadAheadOffset[1].QuadPart;
+        Length = PrivateCacheMap->ReadAheadLength[1];
+        KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+    }
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
 
-    if (!Wait)
+    /* Time to go! */
+    DPRINT("Doing ReadAhead for %p\n", FileObject);
+    /* Lock the file, first */
+    if (!SharedCacheMap->Callbacks->AcquireForReadAhead(SharedCacheMap->LazyWriteContext, FALSE))
+    {
+        Locked = FALSE;
+        goto Clear;
+    }
+
+    /* Remember it's locked */
+    Locked = TRUE;
+
+    /* Don't read past the end of the file */
+    if (CurrentOffset >= SharedCacheMap->FileSize.QuadPart)
+    {
+        goto Clear;
+    }
+    if (CurrentOffset + Length > SharedCacheMap->FileSize.QuadPart)
+    {
+        Length = SharedCacheMap->FileSize.QuadPart - CurrentOffset;
+    }
+
+    /* Next of the algorithm will lock like CcCopyData with the slight
+     * difference that we don't copy data back to an user-backed buffer
+     * We just bring data into Cc
+     */
+    PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
+    if (PartialLength != 0)
     {
-        /* testing, if the requested datas are available */
-        KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
-        current_entry = Bcb->BcbSegmentListHead.Flink;
-        while (current_entry != &Bcb->BcbSegmentListHead)
+        PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
+        Status = CcRosRequestVacb(SharedCacheMap,
+                                  ROUND_DOWN(CurrentOffset,
+                                             VACB_MAPPING_GRANULARITY),
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
+        if (!NT_SUCCESS(Status))
         {
-            CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
-                                         BcbSegmentListEntry);
-            if (!CacheSeg->Valid)
+            DPRINT1("Failed to request VACB: %lx!\n", Status);
+            goto Clear;
+        }
+
+        if (!Valid)
+        {
+            Status = CcReadVirtualAddress(Vacb);
+            if (!NT_SUCCESS(Status))
             {
-                if (((WriteOffset >= CacheSeg->FileOffset) &&
-                     (WriteOffset < CacheSeg->FileOffset + Bcb->CacheSegmentSize))
-                 || ((WriteOffset + Length > CacheSeg->FileOffset) &&
-                     (WriteOffset + Length <= CacheSeg->FileOffset +
-                            Bcb->CacheSegmentSize)))
-                {
-                    KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-                    /* datas not available */
-                    return FALSE;
-                }
+                CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+                DPRINT1("Failed to read data: %lx!\n", Status);
+                goto Clear;
             }
-            current_entry = current_entry->Flink;
         }
-        KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
+
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
+
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
     }
 
-    TempLength = WriteOffset % Bcb->CacheSegmentSize;
-    if (TempLength != 0)
+    while (Length > 0)
     {
-        ULONG ROffset;
-        ROffset = ROUND_DOWN(WriteOffset, Bcb->CacheSegmentSize);
-        TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
-        Status = CcRosRequestCacheSegment(Bcb, ROffset,
-                                          &BaseAddress, &Valid, &CacheSeg);
+        ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
+        PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
+        Status = CcRosRequestVacb(SharedCacheMap,
+                                  CurrentOffset,
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
         if (!NT_SUCCESS(Status))
         {
-            return FALSE;
+            DPRINT1("Failed to request VACB: %lx!\n", Status);
+            goto Clear;
         }
+
         if (!Valid)
         {
-            if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
+            Status = CcReadVirtualAddress(Vacb);
+            if (!NT_SUCCESS(Status))
             {
-                return FALSE;
+                CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
+                DPRINT1("Failed to read data: %lx!\n", Status);
+                goto Clear;
             }
         }
-        memcpy ((char*)BaseAddress + WriteOffset % Bcb->CacheSegmentSize,
-                Buffer, TempLength);
-        CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
 
-        Length -= TempLength;
-        WriteOffset += TempLength;
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
     }
 
-    while (Length > 0)
+Clear:
+    /* See previous comment about private cache map */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+    if (PrivateCacheMap != NULL)
     {
-        TempLength = min (Bcb->CacheSegmentSize, Length);
-        Status = CcRosRequestCacheSegment(Bcb,
-                                          WriteOffset,
-                                          &BaseAddress,
-                                          &Valid,
-                                          &CacheSeg);
-        if (!NT_SUCCESS(Status))
+        /* Mark read ahead as unactive */
+        KeAcquireSpinLockAtDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+        InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
+        KeReleaseSpinLockFromDpcLevel(&PrivateCacheMap->ReadAheadSpinLock);
+    }
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+
+    /* If file was locked, release it */
+    if (Locked)
+    {
+        SharedCacheMap->Callbacks->ReleaseFromReadAhead(SharedCacheMap->LazyWriteContext);
+    }
+
+    /* And drop our extra reference (See: CcScheduleReadAhead) */
+    ObDereferenceObject(FileObject);
+
+    return;
+}
+
+/*
+ * @unimplemented
+ */
+BOOLEAN
+NTAPI
+CcCanIWrite (
+    IN PFILE_OBJECT FileObject,
+    IN ULONG BytesToWrite,
+    IN BOOLEAN Wait,
+    IN BOOLEAN Retrying)
+{
+    KIRQL OldIrql;
+    KEVENT WaitEvent;
+    ULONG Length, Pages;
+    BOOLEAN PerFileDefer;
+    DEFERRED_WRITE Context;
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+    CC_CAN_WRITE_RETRY TryContext;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n",
+        FileObject, BytesToWrite, Wait, Retrying);
+
+    /* Write through is always OK */
+    if (BooleanFlagOn(FileObject->Flags, FO_WRITE_THROUGH))
+    {
+        return TRUE;
+    }
+
+    TryContext = Retrying;
+    /* Allow remote file if not from posted */
+    if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote)
+    {
+        return TRUE;
+    }
+
+    /* Don't exceed max tolerated size */
+    Length = MAX_ZERO_LENGTH;
+    if (BytesToWrite < MAX_ZERO_LENGTH)
+    {
+        Length = BytesToWrite;
+    }
+
+    /* Convert it to pages count */
+    Pages = (Length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+    /* By default, assume limits per file won't be hit */
+    PerFileDefer = FALSE;
+    Fcb = FileObject->FsContext;
+    /* Do we have to check for limits per file? */
+    if (TryContext >= RetryForceCheckPerFile ||
+        BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
+    {
+        /* If master is not locked, lock it now */
+        if (TryContext != RetryMasterLocked)
         {
-            return FALSE;
+            OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
         }
-        if (!Valid && TempLength < Bcb->CacheSegmentSize)
+
+        /* Let's not assume the file is cached... */
+        if (FileObject->SectionObjectPointer != NULL &&
+            FileObject->SectionObjectPointer->SharedCacheMap != NULL)
         {
-            if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
+            SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+            /* Do we have limits per file set? */
+            if (SharedCacheMap->DirtyPageThreshold != 0 &&
+                SharedCacheMap->DirtyPages != 0)
             {
-                CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
-                return FALSE;
+                /* Yes, check whether they are blocking */
+                if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
+                {
+                    PerFileDefer = TRUE;
+                }
             }
         }
-        memcpy (BaseAddress, Buffer, TempLength);
-        CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
-        Length -= TempLength;
-        WriteOffset += TempLength;
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+        /* And don't forget to release master */
+        if (TryContext != RetryMasterLocked)
+        {
+            KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+        }
+    }
+
+    /* So, now allow write if:
+     * - Not the first try or we have no throttling yet
+     * AND:
+     * - We don't exceed threshold!
+     * - We don't exceed what Mm can allow us to use
+     *   + If we're above top, that's fine
+     *   + If we're above bottom with limited modified pages, that's fine
+     *   + Otherwise, throttle!
+     */
+    if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) &&
+        CcTotalDirtyPages + Pages < CcDirtyPageThreshold &&
+        (MmAvailablePages > MmThrottleTop ||
+         (MmModifiedPageListHead.Total < 1000 && MmAvailablePages > MmThrottleBottom)) &&
+        !PerFileDefer)
+    {
+        return TRUE;
+    }
+
+    /* If we can wait, we'll start the wait loop for waiting till we can
+     * write for real
+     */
+    if (!Wait)
+    {
+        return FALSE;
+    }
+
+    /* Otherwise, if there are no deferred writes yet, start the lazy writer */
+    if (IsListEmpty(&CcDeferredWrites))
+    {
+        KIRQL OldIrql;
+
+        OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+        CcScheduleLazyWriteScan(TRUE);
+        KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+    }
+
+    /* Initialize our wait event */
+    KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE);
+
+    /* And prepare a dummy context */
+    Context.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
+    Context.NodeByteSize = sizeof(DEFERRED_WRITE);
+    Context.FileObject = FileObject;
+    Context.BytesToWrite = BytesToWrite;
+    Context.LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
+    Context.Event = &WaitEvent;
+
+    /* And queue it */
+    if (Retrying)
+    {
+        /* To the top, if that's a retry */
+        ExInterlockedInsertHeadList(&CcDeferredWrites,
+                                    &Context.DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
+    }
+    else
+    {
+        /* To the bottom, if that's a first time */
+        ExInterlockedInsertTailList(&CcDeferredWrites,
+                                    &Context.DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
     }
+
+    /* Now, we'll loop until our event is set. When it is set, it means that caller
+     * can immediately write, and has to
+     */
+    do
+    {
+        CcPostDeferredWrites();
+    } while (KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &CcIdleDelay) != STATUS_SUCCESS);
+
     return TRUE;
 }
 
 /*
- * @unimplemented
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+CcCopyRead (
+    IN PFILE_OBJECT FileObject,
+    IN PLARGE_INTEGER FileOffset,
+    IN ULONG Length,
+    IN BOOLEAN Wait,
+    OUT PVOID Buffer,
+    OUT PIO_STATUS_BLOCK IoStatus)
+{
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n",
+        FileObject, FileOffset->QuadPart, Length, Wait);
+
+    DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
+           "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
+           FileObject, FileOffset->QuadPart, Length, Wait,
+           Buffer, IoStatus);
+
+    return CcCopyData(FileObject,
+                      FileOffset->QuadPart,
+                      Buffer,
+                      Length,
+                      CcOperationRead,
+                      Wait,
+                      IoStatus);
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+CcCopyWrite (
+    IN PFILE_OBJECT FileObject,
+    IN PLARGE_INTEGER FileOffset,
+    IN ULONG Length,
+    IN BOOLEAN Wait,
+    IN PVOID Buffer)
+{
+    IO_STATUS_BLOCK IoStatus;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n",
+        FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+
+    DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
+           "Length %lu, Wait %u, Buffer 0x%p)\n",
+           FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+
+    return CcCopyData(FileObject,
+                      FileOffset->QuadPart,
+                      Buffer,
+                      Length,
+                      CcOperationWrite,
+                      Wait,
+                      &IoStatus);
+}
+
+/*
+ * @implemented
  */
 VOID
 NTAPI
@@ -561,7 +851,61 @@ CcDeferWrite (
     IN ULONG BytesToWrite,
     IN BOOLEAN Retrying)
 {
-    UNIMPLEMENTED;
+    KIRQL OldIrql;
+    PDEFERRED_WRITE Context;
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
+        FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
+
+    /* Try to allocate a context for queueing the write operation */
+    Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEFERRED_WRITE), 'CcDw');
+    /* If it failed, immediately execute the operation! */
+    if (Context == NULL)
+    {
+        PostRoutine(Context1, Context2);
+        return;
+    }
+
+    Fcb = FileObject->FsContext;
+
+    /* Otherwise, initialize the context */
+    RtlZeroMemory(Context, sizeof(DEFERRED_WRITE));
+    Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
+    Context->NodeByteSize = sizeof(DEFERRED_WRITE);
+    Context->FileObject = FileObject;
+    Context->PostRoutine = PostRoutine;
+    Context->Context1 = Context1;
+    Context->Context2 = Context2;
+    Context->BytesToWrite = BytesToWrite;
+    Context->LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
+
+    /* And queue it */
+    if (Retrying)
+    {
+        /* To the top, if that's a retry */
+        ExInterlockedInsertHeadList(&CcDeferredWrites,
+                                    &Context->DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
+    }
+    else
+    {
+        /* To the bottom, if that's a first time */
+        ExInterlockedInsertTailList(&CcDeferredWrites,
+                                    &Context->DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
+    }
+
+    /* Try to execute the posted writes */
+    CcPostDeferredWrites();
+
+    /* Schedule a lazy writer run to handle deferred writes */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    if (!LazyWriter.ScanActive)
+    {
+        CcScheduleLazyWriteScan(FALSE);
+    }
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
 }
 
 /*
@@ -577,8 +921,24 @@ CcFastCopyRead (
     OUT PVOID Buffer,
     OUT PIO_STATUS_BLOCK IoStatus)
 {
-    UNIMPLEMENTED;
+    LARGE_INTEGER LargeFileOffset;
+    BOOLEAN Success;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n",
+        FileObject, FileOffset, Length, PageCount, Buffer);
+
+    DBG_UNREFERENCED_PARAMETER(PageCount);
+
+    LargeFileOffset.QuadPart = FileOffset;
+    Success = CcCopyRead(FileObject,
+                         &LargeFileOffset,
+                         Length,
+                         TRUE,
+                         Buffer,
+                         IoStatus);
+    ASSERT(Success == TRUE);
 }
+
 /*
  * @unimplemented
  */
@@ -590,19 +950,19 @@ CcFastCopyWrite (
     IN ULONG Length,
     IN PVOID Buffer)
 {
-    UNIMPLEMENTED;
-}
-
-/*
- * @unimplemented
- */
-NTSTATUS
-NTAPI
-CcWaitForCurrentLazyWriterActivity (
-    VOID)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    LARGE_INTEGER LargeFileOffset;
+    BOOLEAN Success;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n",
+        FileObject, FileOffset, Length, Buffer);
+
+    LargeFileOffset.QuadPart = FileOffset;
+    Success = CcCopyWrite(FileObject,
+                          &LargeFileOffset,
+                          Length,
+                          TRUE,
+                          Buffer);
+    ASSERT(Success == TRUE);
 }
 
 /*
@@ -618,18 +978,21 @@ CcZeroData (
 {
     NTSTATUS Status;
     LARGE_INTEGER WriteOffset;
-    ULONG Length;
+    LONGLONG Length;
     ULONG CurrentLength;
     PMDL Mdl;
     ULONG i;
     IO_STATUS_BLOCK Iosb;
     KEVENT Event;
 
+    CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n",
+        FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait);
+
     DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, "
            "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
            Wait);
 
-    Length = EndOffset->u.LowPart - StartOffset->u.LowPart;
+    Length = EndOffset->QuadPart - StartOffset->QuadPart;
     WriteOffset.QuadPart = StartOffset->QuadPart;
 
     if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
@@ -640,9 +1003,9 @@ CcZeroData (
 
         while (Length > 0)
         {
-            if (Length + WriteOffset.u.LowPart % PAGE_SIZE > MAX_ZERO_LENGTH)
+            if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH)
             {
-                CurrentLength = MAX_ZERO_LENGTH - WriteOffset.u.LowPart % PAGE_SIZE;
+                CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE;
             }
             else
             {
@@ -675,100 +1038,15 @@ CcZeroData (
     }
     else
     {
-        /* File is cached */
-        KIRQL oldirql;
-        PBCB Bcb;
-        PLIST_ENTRY current_entry;
-        PCACHE_SEGMENT CacheSeg, current, previous;
-        ULONG TempLength;
-
-        Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
-        if (Wait)
-        {
-            /* testing, if the requested datas are available */
-            KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
-            current_entry = Bcb->BcbSegmentListHead.Flink;
-            while (current_entry != &Bcb->BcbSegmentListHead)
-            {
-                CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
-                                             BcbSegmentListEntry);
-                if (!CacheSeg->Valid)
-                {
-                    if (((WriteOffset.u.LowPart >= CacheSeg->FileOffset) &&
-                         (WriteOffset.u.LowPart < CacheSeg->FileOffset + Bcb->CacheSegmentSize))
-                     || ((WriteOffset.u.LowPart + Length > CacheSeg->FileOffset) &&
-                         (WriteOffset.u.LowPart + Length <=
-                                CacheSeg->FileOffset + Bcb->CacheSegmentSize)))
-                    {
-                        KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-                        /* datas not available */
-                        return FALSE;
-                    }
-                }
-                current_entry = current_entry->Flink;
-            }
-            KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-        }
-
-        while (Length > 0)
-        {
-            ULONG Offset;
-            Offset = WriteOffset.u.LowPart % Bcb->CacheSegmentSize;
-            if (Length + Offset > MAX_ZERO_LENGTH)
-            {
-                CurrentLength = MAX_ZERO_LENGTH - Offset;
-            }
-            else
-            {
-                CurrentLength = Length;
-            }
-            Status = CcRosGetCacheSegmentChain (Bcb, WriteOffset.u.LowPart - Offset,
-                                                Offset + CurrentLength, &CacheSeg);
-            if (!NT_SUCCESS(Status))
-            {
-                return FALSE;
-            }
-            current = CacheSeg;
-
-            while (current != NULL)
-            {
-                Offset = WriteOffset.u.LowPart % Bcb->CacheSegmentSize;
-                if ((Offset != 0) ||
-                    (Offset + CurrentLength < Bcb->CacheSegmentSize))
-                {
-                    if (!current->Valid)
-                    {
-                        /* read the segment */
-                        Status = ReadCacheSegment(current);
-                        if (!NT_SUCCESS(Status))
-                        {
-                            DPRINT1("ReadCacheSegment failed, status %x\n",
-                                    Status);
-                        }
-                    }
-                    TempLength = min (CurrentLength, Bcb->CacheSegmentSize - Offset);
-                }
-                else
-                {
-                    TempLength = Bcb->CacheSegmentSize;
-                }
-                memset ((PUCHAR)current->BaseAddress + Offset, 0, TempLength);
-
-                WriteOffset.QuadPart += TempLength;
-                CurrentLength -= TempLength;
-                Length -= TempLength;
-
-                current = current->NextInChain;
-            }
-
-            current = CacheSeg;
-            while (current != NULL)
-            {
-                previous = current;
-                current = current->NextInChain;
-                CcRosReleaseCacheSegment(Bcb, previous, TRUE, TRUE, FALSE);
-            }
-        }
+        IO_STATUS_BLOCK IoStatus;
+
+        return CcCopyData(FileObject,
+                          WriteOffset.QuadPart,
+                          NULL,
+                          Length,
+                          CcOperationZero,
+                          Wait,
+                          &IoStatus);
     }
 
     return TRUE;