[NTOS:CC] CcCanIWrite(): Use BYTES_TO_PAGES(Length)
[reactos.git] / ntoskrnl / cc / copy.c
index d63c36e..41980b8 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)
-C_ASSERT(MAX_RW_LENGTH <= VACB_MAPPING_GRANULARITY);
 
+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;
@@ -28,6 +43,13 @@ ULONG CcFastReadWait;
 ULONG CcFastReadNoWait;
 ULONG CcFastReadResourceMiss;
 
+/* Counters:
+ * - Amount of pages flushed to the disk
+ * - Number of flush operations
+ */
+ULONG CcDataPages = 0;
+ULONG CcDataFlushes = 0;
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
@@ -54,136 +76,6 @@ CcInitCacheZeroPage (
     MiZeroPhysicalPage(CcZeroPage);
 }
 
-NTSTATUS
-NTAPI
-ReadVacbChain (
-    PROS_SHARED_CACHE_MAP SharedCacheMap,
-    ULONG ReadOffset,
-    ULONG Length,
-    PVOID Buffer)
-{
-    PROS_VACB head;
-    PROS_VACB current;
-    PROS_VACB previous;
-    IO_STATUS_BLOCK Iosb;
-    NTSTATUS Status;
-    ULONG TempLength;
-    KEVENT Event;
-    PMDL Mdl;
-
-    Mdl = _alloca(MmSizeOfMdl(NULL, MAX_RW_LENGTH));
-
-    Status = CcRosGetVacbChain(SharedCacheMap, ReadOffset, Length, &head);
-    if (!NT_SUCCESS(Status))
-    {
-        return Status;
-    }
-    current = head;
-    while (current != NULL)
-    {
-        /*
-         * If the current VACB is valid then copy it into the user buffer.
-         */
-        if (current->Valid)
-        {
-            TempLength = min(VACB_MAPPING_GRANULARITY, Length);
-            RtlCopyMemory(Buffer, current->BaseAddress, TempLength);
-
-            Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
-
-            Length = Length - TempLength;
-            previous = current;
-            current = current->NextInChain;
-            CcRosReleaseVacb(SharedCacheMap, previous, TRUE, FALSE, FALSE);
-        }
-        /*
-         * Otherwise read in as much as we can.
-         */
-        else
-        {
-            PROS_VACB current2;
-            ULONG current_size;
-            ULONG i;
-            PPFN_NUMBER MdlPages;
-
-            /*
-             * Count the maximum number of bytes we could read starting
-             * from the current VACB.
-             */
-            current2 = current;
-            current_size = 0;
-            while ((current2 != NULL) && !current2->Valid && (current_size < MAX_RW_LENGTH))
-            {
-                current2 = current2->NextInChain;
-                current_size += VACB_MAPPING_GRANULARITY;
-            }
-
-            /*
-             * 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 < VACB_MAPPING_GRANULARITY / PAGE_SIZE; i++, address = RVA(address, PAGE_SIZE))
-                {
-                    *MdlPages++ = MmGetPfnForProcess(NULL, address);
-                }
-                current2 = current2->NextInChain;
-                current_size += VACB_MAPPING_GRANULARITY;
-            }
-
-            /*
-             * Read in the information.
-             */
-            KeInitializeEvent(&Event, NotificationEvent, FALSE);
-            Status = IoPageRead(SharedCacheMap->FileObject,
-                                Mdl,
-                                &current->FileOffset,
-                                &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;
-                    CcRosReleaseVacb(SharedCacheMap, 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(VACB_MAPPING_GRANULARITY, Length);
-                RtlCopyMemory(Buffer, previous->BaseAddress, TempLength);
-
-                Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
-
-                Length = Length - TempLength;
-                CcRosReleaseVacb(SharedCacheMap, previous, TRUE, FALSE, FALSE);
-                current_size += VACB_MAPPING_GRANULARITY;
-            }
-        }
-    }
-    return STATUS_SUCCESS;
-}
-
 NTSTATUS
 NTAPI
 CcReadVirtualAddress (
@@ -194,12 +86,18 @@ CcReadVirtualAddress (
     NTSTATUS Status;
     IO_STATUS_BLOCK IoStatus;
     KEVENT Event;
+    ULARGE_INTEGER LargeSize;
 
-    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
-    if (Size > VACB_MAPPING_GRANULARITY)
+    LargeSize.QuadPart = Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart;
+    if (LargeSize.QuadPart > VACB_MAPPING_GRANULARITY)
     {
-        Size = VACB_MAPPING_GRANULARITY;
+        LargeSize.QuadPart = VACB_MAPPING_GRANULARITY;
     }
+    Size = LargeSize.LowPart;
+
+    Size = ROUND_TO_PAGES(Size);
+    ASSERT(Size <= VACB_MAPPING_GRANULARITY);
+    ASSERT(Size > 0);
 
     Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
     if (!Mdl)
@@ -207,14 +105,30 @@ CcReadVirtualAddress (
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    MmBuildMdlForNonPagedPool(Mdl);
-    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress);
+        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);
@@ -244,13 +158,14 @@ CcWriteVirtualAddress (
     NTSTATUS Status;
     IO_STATUS_BLOCK IoStatus;
     KEVENT Event;
+    ULARGE_INTEGER LargeSize;
 
-    Vacb->Dirty = FALSE;
-    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
-    if (Size > VACB_MAPPING_GRANULARITY)
+    LargeSize.QuadPart = Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart;
+    if (LargeSize.QuadPart > VACB_MAPPING_GRANULARITY)
     {
-        Size = VACB_MAPPING_GRANULARITY;
+        LargeSize.QuadPart = VACB_MAPPING_GRANULARITY;
     }
+    Size = LargeSize.LowPart;
     //
     // Nonpaged pool PDEs in ReactOS must actually be synchronized between the
     // MmGlobalPageDirectory and the real system PDE directory. What a mess...
@@ -263,298 +178,690 @@ CcWriteVirtualAddress (
         } while (++i < (Size >> PAGE_SHIFT));
     }
 
+    Size = ROUND_TO_PAGES(Size);
+    ASSERT(Size <= VACB_MAPPING_GRANULARITY);
+    ASSERT(Size > 0);
+
     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(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        DPRINT1("MmProbeAndLockPages failed with: %lx for %p (%p, %p)\n", Status, Mdl, Vacb, Vacb->BaseAddress);
+        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);
-        Vacb->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;
-    PVOID BaseAddress;
+    NTSTATUS Status;
+    LONGLONG CurrentOffset;
+    ULONG BytesCopied;
+    KIRQL OldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+    PLIST_ENTRY ListEntry;
     PROS_VACB Vacb;
+    ULONG PartialLength;
+    PVOID BaseAddress;
     BOOLEAN Valid;
-    ULONG ReadLength = 0;
-    PROS_SHARED_CACHE_MAP SharedCacheMap;
-    KIRQL oldirql;
-    PLIST_ENTRY current_entry;
-    PROS_VACB 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);
+    PPRIVATE_CACHE_MAP PrivateCacheMap;
 
     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
-    ReadOffset = (ULONG)FileOffset->QuadPart;
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+    CurrentOffset = FileOffset;
+    BytesCopied = 0;
 
-    DPRINT("SectionSize %I64d, FileSize %I64d\n",
-           SharedCacheMap->SectionSize.QuadPart,
-           SharedCacheMap->FileSize.QuadPart);
-
-    /*
-     * Check for the nowait case that all the cache VACBs that would
-     * cover this read are in memory.
-     */
     if (!Wait)
     {
-        KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldirql);
+        /* 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 */
-        current_entry = SharedCacheMap->CacheMapVacbListHead.Flink;
-        while (current_entry != &SharedCacheMap->CacheMapVacbListHead)
+        ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
+        while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
         {
-            current = CONTAINING_RECORD(current_entry,
-                                        ROS_VACB,
-                                        CacheMapVacbListEntry);
-            if (!current->Valid &&
-                DoRangesIntersect(current->FileOffset.QuadPart,
+            Vacb = CONTAINING_RECORD(ListEntry,
+                                     ROS_VACB,
+                                     CacheMapVacbListEntry);
+            ListEntry = ListEntry->Flink;
+            if (!Vacb->Valid &&
+                DoRangesIntersect(Vacb->FileOffset.QuadPart,
                                   VACB_MAPPING_GRANULARITY,
-                                  ReadOffset, Length))
+                                  CurrentOffset, Length))
             {
-                KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
-                IoStatus->Status = STATUS_UNSUCCESSFUL;
-                IoStatus->Information = 0;
+                KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
+                /* data not available */
                 return FALSE;
             }
-            if (current->FileOffset.QuadPart >= ReadOffset + Length)
+            if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length)
                 break;
-            current_entry = current_entry->Flink;
         }
-        KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
+        KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
     }
 
-    TempLength = ReadOffset % VACB_MAPPING_GRANULARITY;
-    if (TempLength != 0)
+    PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
+    if (PartialLength != 0)
     {
-        TempLength = min(Length, VACB_MAPPING_GRANULARITY - TempLength);
+        PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
         Status = CcRosRequestVacb(SharedCacheMap,
-                                  ROUND_DOWN(ReadOffset,
+                                  ROUND_DOWN(CurrentOffset,
                                              VACB_MAPPING_GRANULARITY),
-                                  &BaseAddress, &Valid, &Vacb);
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
         if (!NT_SUCCESS(Status))
-        {
-            IoStatus->Information = 0;
-            IoStatus->Status = Status;
-            DPRINT("CcRosRequestVacb failed, Status %x\n", Status);
-            return FALSE;
-        }
+            ExRaiseStatus(Status);
         if (!Valid)
         {
             Status = CcReadVirtualAddress(Vacb);
             if (!NT_SUCCESS(Status))
             {
-                IoStatus->Information = 0;
-                IoStatus->Status = Status;
                 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
-                return FALSE;
+                ExRaiseStatus(Status);
             }
         }
-        RtlCopyMemory(Buffer,
-                      (char*)BaseAddress + ReadOffset % VACB_MAPPING_GRANULARITY,
-                      TempLength);
-        CcRosReleaseVacb(SharedCacheMap, Vacb, 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(VACB_MAPPING_GRANULARITY, Length);
-        Status = ReadVacbChain(SharedCacheMap, 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("ReadVacbChain 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);
+
+        if (!NT_SUCCESS(Status))
+            ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
 
-        ReadLength += TempLength;
-        Length -= TempLength;
-        ReadOffset += TempLength;
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
+        BytesCopied += PartialLength;
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+        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 and next read may get us cross VACB boundary,
+         * schedule next read
+         */
+        if (!BooleanFlagOn(FileObject->Flags, FO_RANDOM_ACCESS) &&
+            (CurrentOffset - 1) / VACB_MAPPING_GRANULARITY != (CurrentOffset + BytesCopied - 1) / VACB_MAPPING_GRANULARITY)
+        {
+            CcScheduleReadAhead(FileObject, (PLARGE_INTEGER)&FileOffset, BytesCopied);
+        }
+
+        /* 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;
+    LONGLONG CurrentOffset;
+    KIRQL OldIrql;
     PROS_SHARED_CACHE_MAP SharedCacheMap;
-    PLIST_ENTRY current_entry;
     PROS_VACB Vacb;
-    ULONG TempLength;
+    ULONG PartialLength;
     PVOID BaseAddress;
     BOOLEAN Valid;
-
-    DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
-           "Length %lu, Wait %u, Buffer 0x%p)\n",
-           FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+    ULONG Length;
+    PPRIVATE_CACHE_MAP PrivateCacheMap;
+    BOOLEAN Locked;
 
     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
-    WriteOffset = (ULONG)FileOffset->QuadPart;
 
-    if (!Wait)
+    /* 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)
     {
-        /* testing, if the requested datas are available */
-        KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldirql);
-        /* FIXME: this loop doesn't take into account areas that don't have
-         * a VACB in the list yet */
-        current_entry = SharedCacheMap->CacheMapVacbListHead.Flink;
-        while (current_entry != &SharedCacheMap->CacheMapVacbListHead)
-        {
-            Vacb = CONTAINING_RECORD(current_entry,
-                                     ROS_VACB,
-                                     CacheMapVacbListEntry);
-            if (!Vacb->Valid &&
-                DoRangesIntersect(Vacb->FileOffset.QuadPart,
-                                  VACB_MAPPING_GRANULARITY,
-                                  WriteOffset, Length))
-            {
-                KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
-                /* datas not available */
-                return FALSE;
-            }
-            if (Vacb->FileOffset.QuadPart >= WriteOffset + Length)
-                break;
-            current_entry = current_entry->Flink;
-        }
-        KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
+        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);
+
+    /* 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;
     }
 
-    TempLength = WriteOffset % VACB_MAPPING_GRANULARITY;
-    if (TempLength != 0)
+    /* 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)
     {
-        ULONG ROffset;
-        ROffset = ROUND_DOWN(WriteOffset, VACB_MAPPING_GRANULARITY);
-        TempLength = min(Length, VACB_MAPPING_GRANULARITY - TempLength);
-        Status = CcRosRequestVacb(SharedCacheMap, ROffset,
-                                  &BaseAddress, &Valid, &Vacb);
+        PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
+        Status = CcRosRequestVacb(SharedCacheMap,
+                                  ROUND_DOWN(CurrentOffset,
+                                             VACB_MAPPING_GRANULARITY),
+                                  &BaseAddress,
+                                  &Valid,
+                                  &Vacb);
         if (!NT_SUCCESS(Status))
         {
-            return FALSE;
+            DPRINT1("Failed to request VACB: %lx!\n", Status);
+            goto Clear;
         }
+
         if (!Valid)
         {
-            if (!NT_SUCCESS(CcReadVirtualAddress(Vacb)))
+            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;
             }
         }
-        RtlCopyMemory((char*)BaseAddress + WriteOffset % VACB_MAPPING_GRANULARITY,
-                      Buffer,
-                      TempLength);
-        CcRosReleaseVacb(SharedCacheMap, Vacb, 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)
     {
-        TempLength = min(VACB_MAPPING_GRANULARITY, Length);
+        ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
+        PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
         Status = CcRosRequestVacb(SharedCacheMap,
-                                  WriteOffset,
+                                  CurrentOffset,
                                   &BaseAddress,
                                   &Valid,
                                   &Vacb);
         if (!NT_SUCCESS(Status))
         {
-            return FALSE;
+            DPRINT1("Failed to request VACB: %lx!\n", Status);
+            goto Clear;
         }
-        if (!Valid && TempLength < VACB_MAPPING_GRANULARITY)
+
+        if (!Valid)
         {
-            if (!NT_SUCCESS(CcReadVirtualAddress(Vacb)))
+            Status = CcReadVirtualAddress(Vacb);
+            if (!NT_SUCCESS(Status))
             {
                 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
-                return FALSE;
+                DPRINT1("Failed to read data: %lx!\n", Status);
+                goto Clear;
             }
         }
-        RtlCopyMemory(BaseAddress, Buffer, TempLength);
-        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, TRUE, FALSE);
-        Length -= TempLength;
-        WriteOffset += TempLength;
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
+
+        Length -= PartialLength;
+        CurrentOffset += PartialLength;
     }
-    return TRUE;
+
+Clear:
+    /* See previous comment about private cache map */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+    if (PrivateCacheMap != NULL)
+    {
+        /* 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;
+    }
+
+    Pages = BYTES_TO_PAGES(Length);
+
+    /* 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)
+        {
+            OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+        }
+
+        /* Let's not assume the file is cached... */
+        if (FileObject->SectionObjectPointer != NULL &&
+            FileObject->SectionObjectPointer->SharedCacheMap != NULL)
+        {
+            SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+            /* Do we have limits per file set? */
+            if (SharedCacheMap->DirtyPageThreshold != 0 &&
+                SharedCacheMap->DirtyPages != 0)
+            {
+                /* Yes, check whether they are blocking */
+                if (Pages + SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
+                {
+                    PerFileDefer = TRUE;
+                }
+            }
+        }
+
+        /* 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);
+    }
+
+    DPRINT1("Actively deferring write for: %p\n", FileObject);
+    /* 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;
+}
+
+/*
+ * @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
 CcDeferWrite (
@@ -565,7 +872,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);
 }
 
 /*
@@ -581,8 +942,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
  */
@@ -594,19 +971,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);
 }
 
 /*
@@ -622,18 +999,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)
@@ -644,9 +1024,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
             {
@@ -679,102 +1059,15 @@ CcZeroData (
     }
     else
     {
-        /* File is cached */
-        KIRQL oldirql;
-        PROS_SHARED_CACHE_MAP SharedCacheMap;
-        PLIST_ENTRY current_entry;
-        PROS_VACB Vacb, current, previous;
-        ULONG TempLength;
-
-        SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
-        if (!Wait)
-        {
-            /* testing, if the requested datas are available */
-            KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldirql);
-            /* FIXME: this loop doesn't take into account areas that don't have
-             * a VACB in the list yet */
-            current_entry = SharedCacheMap->CacheMapVacbListHead.Flink;
-            while (current_entry != &SharedCacheMap->CacheMapVacbListHead)
-            {
-                Vacb = CONTAINING_RECORD(current_entry,
-                                         ROS_VACB,
-                                         CacheMapVacbListEntry);
-                if (!Vacb->Valid &&
-                    DoRangesIntersect(Vacb->FileOffset.QuadPart,
-                                      VACB_MAPPING_GRANULARITY,
-                                      WriteOffset.u.LowPart, Length))
-                {
-                    KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
-                    /* datas not available */
-                    return FALSE;
-                }
-                if (Vacb->FileOffset.QuadPart >= WriteOffset.u.LowPart + Length)
-                    break;
-                current_entry = current_entry->Flink;
-            }
-            KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
-        }
-
-        while (Length > 0)
-        {
-            ULONG Offset;
-            Offset = WriteOffset.u.LowPart % VACB_MAPPING_GRANULARITY;
-            if (Length + Offset > MAX_ZERO_LENGTH)
-            {
-                CurrentLength = MAX_ZERO_LENGTH - Offset;
-            }
-            else
-            {
-                CurrentLength = Length;
-            }
-            Status = CcRosGetVacbChain(SharedCacheMap, WriteOffset.u.LowPart - Offset,
-                                       Offset + CurrentLength, &Vacb);
-            if (!NT_SUCCESS(Status))
-            {
-                return FALSE;
-            }
-            current = Vacb;
-
-            while (current != NULL)
-            {
-                Offset = WriteOffset.u.LowPart % VACB_MAPPING_GRANULARITY;
-                if ((Offset != 0) ||
-                    (Offset + CurrentLength < VACB_MAPPING_GRANULARITY))
-                {
-                    if (!current->Valid)
-                    {
-                        /* read the block */
-                        Status = CcReadVirtualAddress(current);
-                        if (!NT_SUCCESS(Status))
-                        {
-                            DPRINT1("CcReadVirtualAddress failed, status %x\n",
-                                    Status);
-                        }
-                    }
-                    TempLength = min(CurrentLength, VACB_MAPPING_GRANULARITY - Offset);
-                }
-                else
-                {
-                    TempLength = VACB_MAPPING_GRANULARITY;
-                }
-                RtlZeroMemory((PUCHAR)current->BaseAddress + Offset,
-                              TempLength);
-
-                WriteOffset.QuadPart += TempLength;
-                CurrentLength -= TempLength;
-                Length -= TempLength;
-
-                current = current->NextInChain;
-            }
-
-            current = Vacb;
-            while (current != NULL)
-            {
-                previous = current;
-                current = current->NextInChain;
-                CcRosReleaseVacb(SharedCacheMap, previous, TRUE, TRUE, FALSE);
-            }
-        }
+        IO_STATUS_BLOCK IoStatus;
+
+        return CcCopyData(FileObject,
+                          WriteOffset.QuadPart,
+                          NULL,
+                          Length,
+                          CcOperationZero,
+                          Wait,
+                          &IoStatus);
     }
 
     return TRUE;