[NTOSKRNL] Simplify (and speedup!) CcCanIWrite() using the dirty pages count in cache...
[reactos.git] / ntoskrnl / cc / copy.c
index 697272a..e194074 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;
+
+ULONG CcRosTraceLevel = 0;
 ULONG CcFastMdlReadWait;
 ULONG CcFastMdlReadNotPossible;
 ULONG CcFastReadNotPossible;
@@ -28,6 +35,8 @@ ULONG CcFastReadWait;
 ULONG CcFastReadNoWait;
 ULONG CcFastReadResourceMiss;
 
+extern KEVENT iLazyWriterNotify;
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
@@ -56,170 +65,53 @@ CcInitCacheZeroPage (
 
 NTSTATUS
 NTAPI
-ReadCacheSegmentChain (
-    PBCB Bcb,
-    ULONG ReadOffset,
-    ULONG Length,
-    PVOID Buffer)
+CcReadVirtualAddress (
+    PROS_VACB Vacb)
 {
-    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(VACB_MAPPING_GRANULARITY, Length);
-            RtlCopyMemory(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 += 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.
-             */
-            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(VACB_MAPPING_GRANULARITY, Length);
-                RtlCopyMemory(Buffer, previous->BaseAddress, TempLength);
-
-                Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
-
-                Length = Length - TempLength;
-                CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
-                current_size += VACB_MAPPING_GRANULARITY;
-            }
-        }
-    }
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS
-NTAPI
-ReadCacheSegment (
-    PCACHE_SEGMENT CacheSeg)
-{
-    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);
+    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
     if (Size > VACB_MAPPING_GRANULARITY)
     {
         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
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(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);
@@ -232,7 +124,7 @@ ReadCacheSegment (
 
     if (Size < VACB_MAPPING_GRANULARITY)
     {
-        RtlZeroMemory((char*)CacheSeg->BaseAddress + Size,
+        RtlZeroMemory((char*)Vacb->BaseAddress + Size,
                       VACB_MAPPING_GRANULARITY - Size);
     }
 
@@ -241,19 +133,17 @@ 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);
+    Vacb->Dirty = FALSE;
+    Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
     if (Size > VACB_MAPPING_GRANULARITY)
     {
         Size = VACB_MAPPING_GRANULARITY;
@@ -266,297 +156,335 @@ 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
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(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;
+        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;
+    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;
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    CurrentOffset = FileOffset;
+    BytesCopied = 0;
 
-    DPRINT("AllocationSize %I64d, FileSize %I64d\n",
-           Bcb->AllocationSize.QuadPart,
-           Bcb->FileSize.QuadPart);
-
-    /*
-     * Check for the nowait case that all the cache segments that would
-     * cover this read are in memory.
-     */
     if (!Wait)
     {
-        KeAcquireSpinLock(&Bcb->BcbLock, &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 segment in the list yet */
-        current_entry = Bcb->BcbSegmentListHead.Flink;
-        while (current_entry != &Bcb->BcbSegmentListHead)
+         * 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 &&
-                DoSegmentsIntersect(current->FileOffset, VACB_MAPPING_GRANULARITY,
-                                    ReadOffset, Length))
+            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;
             }
-            if (current->FileOffset >= ReadOffset + Length)
+            if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length)
                 break;
-            current_entry = current_entry->Flink;
         }
-        KeReleaseSpinLock(&Bcb->BcbLock, 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);
-        Status = CcRosRequestCacheSegment(Bcb,
-                                          ROUND_DOWN(ReadOffset,
-                                                     VACB_MAPPING_GRANULARITY),
-                                          &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);
             }
         }
-        RtlCopyMemory(Buffer,
-                      (char*)BaseAddress + ReadOffset % VACB_MAPPING_GRANULARITY,
-                      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(VACB_MAPPING_GRANULARITY, 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);
 
-        ReadLength += TempLength;
-        Length -= TempLength;
-        ReadOffset += TempLength;
+        CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + 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);
+    }
     IoStatus->Status = STATUS_SUCCESS;
-    IoStatus->Information = ReadLength;
-    DPRINT("CcCopyRead O.K.\n");
+    IoStatus->Information = BytesCopied;
     return TRUE;
 }
 
 /*
- * @implemented
+ * @unimplemented
  */
 BOOLEAN
 NTAPI
-CcCopyWrite (
+CcCanIWrite (
     IN PFILE_OBJECT FileObject,
-    IN PLARGE_INTEGER FileOffset,
-    IN ULONG Length,
+    IN ULONG BytesToWrite,
     IN BOOLEAN Wait,
-    IN PVOID Buffer)
+    IN BOOLEAN Retrying)
 {
-    NTSTATUS Status;
-    ULONG WriteOffset;
-    KIRQL oldirql;
-    PBCB Bcb;
-    PLIST_ENTRY current_entry;
-    PCACHE_SEGMENT CacheSeg;
-    ULONG TempLength;
-    PVOID BaseAddress;
-    BOOLEAN Valid;
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
 
-    DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
-           "Length %lu, Wait %u, Buffer 0x%p)\n",
-           FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+    CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n",
+        FileObject, BytesToWrite, Wait, Retrying);
 
-    Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
-    WriteOffset = (ULONG)FileOffset->QuadPart;
-
-    if (!Wait)
+    /* We cannot write if dirty pages count is above threshold */
+    if (CcTotalDirtyPages > CcDirtyPageThreshold)
     {
-        /* testing, if the requested datas are available */
-        KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
-        /* FIXME: this loop doesn't take into account areas that don't have
-         * a segment in the list yet */
-        current_entry = Bcb->BcbSegmentListHead.Flink;
-        while (current_entry != &Bcb->BcbSegmentListHead)
-        {
-            CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
-                                         BcbSegmentListEntry);
-            if (!CacheSeg->Valid &&
-                DoSegmentsIntersect(CacheSeg->FileOffset, VACB_MAPPING_GRANULARITY,
-                                    WriteOffset, Length))
-            {
-                KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-                /* datas not available */
-                return FALSE;
-            }
-            if (CacheSeg->FileOffset >= WriteOffset + Length)
-                break;
-            current_entry = current_entry->Flink;
-        }
-        KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
+        return FALSE;
     }
 
-    TempLength = WriteOffset % VACB_MAPPING_GRANULARITY;
-    if (TempLength != 0)
+    /* We cannot write if dirty pages count will bring use above
+     * XXX: Might not be accurate
+     */
+    if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
     {
-        ULONG ROffset;
-        ROffset = ROUND_DOWN(WriteOffset, VACB_MAPPING_GRANULARITY);
-        TempLength = min(Length, VACB_MAPPING_GRANULARITY - TempLength);
-        Status = CcRosRequestCacheSegment(Bcb, ROffset,
-                                          &BaseAddress, &Valid, &CacheSeg);
-        if (!NT_SUCCESS(Status))
-        {
-            return FALSE;
-        }
-        if (!Valid)
-        {
-            if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
-            {
-                return FALSE;
-            }
-        }
-        RtlCopyMemory((char*)BaseAddress + WriteOffset % VACB_MAPPING_GRANULARITY,
-                      Buffer,
-                      TempLength);
-        CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
-
-        Length -= TempLength;
-        WriteOffset += TempLength;
+        return FALSE;
+    }
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+    /* Is there a limit per file object? */
+    Fcb = FileObject->FsContext;
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES) ||
+        SharedCacheMap->DirtyPageThreshold == 0)
+    {
+        /* Nope, so that's fine, allow write operation */
+        return TRUE;
     }
 
-    while (Length > 0)
+    /* Is dirty page count above local threshold? */
+    if (SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
     {
-        TempLength = min(VACB_MAPPING_GRANULARITY, Length);
-        Status = CcRosRequestCacheSegment(Bcb,
-                                          WriteOffset,
-                                          &BaseAddress,
-                                          &Valid,
-                                          &CacheSeg);
-        if (!NT_SUCCESS(Status))
-        {
-            return FALSE;
-        }
-        if (!Valid && TempLength < VACB_MAPPING_GRANULARITY)
-        {
-            if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
-            {
-                CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
-                return FALSE;
-            }
-        }
-        RtlCopyMemory(BaseAddress, Buffer, TempLength);
-        CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
-        Length -= TempLength;
-        WriteOffset += TempLength;
+        return FALSE;
+    }
 
-        Buffer = (PVOID)((ULONG_PTR)Buffer + TempLength);
+    /* We cannot write if dirty pages count will bring use above
+     * XXX: Might not be accurate
+     */
+    if (SharedCacheMap->DirtyPages + (BytesToWrite / PAGE_SIZE) > SharedCacheMap->DirtyPageThreshold)
+    {
+        return FALSE;
     }
+
     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
@@ -568,7 +496,43 @@ CcDeferWrite (
     IN ULONG BytesToWrite,
     IN BOOLEAN Retrying)
 {
-    UNIMPLEMENTED;
+    PROS_DEFERRED_WRITE_CONTEXT Context;
+
+    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(ROS_DEFERRED_WRITE_CONTEXT), 'CcDw');
+    /* If it failed, immediately execute the operation! */
+    if (Context == NULL)
+    {
+        PostRoutine(Context1, Context2);
+        return;
+    }
+
+    /* Otherwise, initialize the context */
+    Context->FileObject = FileObject;
+    Context->PostRoutine = PostRoutine;
+    Context->Context1 = Context1;
+    Context->Context2 = Context2;
+    Context->BytesToWrite = BytesToWrite;
+    Context->Retrying = Retrying;
+
+    /* And queue it */
+    if (Retrying)
+    {
+        /* To the top, if that's a retry */
+        ExInterlockedInsertHeadList(&CcDeferredWrites,
+                                    &Context->CcDeferredWritesEntry,
+                                    &CcDeferredWriteSpinLock);
+    }
+    else
+    {
+        /* To the bottom, if that's a first time */
+        ExInterlockedInsertTailList(&CcDeferredWrites,
+                                    &Context->CcDeferredWritesEntry,
+                                    &CcDeferredWriteSpinLock);
+    }
 }
 
 /*
@@ -584,8 +548,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
  */
@@ -597,19 +577,43 @@ CcFastCopyWrite (
     IN ULONG Length,
     IN PVOID Buffer)
 {
-    UNIMPLEMENTED;
+    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);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
 CcWaitForCurrentLazyWriterActivity (
     VOID)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+
+    /* Lazy writer is done when its event is set */
+    Status = KeWaitForSingleObject(&iLazyWriterNotify,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    return STATUS_SUCCESS;
 }
 
 /*
@@ -625,18 +629,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)
@@ -647,9 +654,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
             {
@@ -682,100 +689,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);
-            /* FIXME: this loop doesn't take into account areas that don't have
-             * a segment in the list yet */
-            current_entry = Bcb->BcbSegmentListHead.Flink;
-            while (current_entry != &Bcb->BcbSegmentListHead)
-            {
-                CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
-                                             BcbSegmentListEntry);
-                if (!CacheSeg->Valid &&
-                    DoSegmentsIntersect(CacheSeg->FileOffset, VACB_MAPPING_GRANULARITY,
-                                        WriteOffset.u.LowPart, Length))
-                {
-                    KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
-                    /* datas not available */
-                    return FALSE;
-                }
-                if (CacheSeg->FileOffset >= WriteOffset.u.LowPart + Length)
-                    break;
-                current_entry = current_entry->Flink;
-            }
-            KeReleaseSpinLock(&Bcb->BcbLock, 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 = CcRosGetCacheSegmentChain (Bcb, WriteOffset.u.LowPart - Offset,
-                                                Offset + CurrentLength, &CacheSeg);
-            if (!NT_SUCCESS(Status))
-            {
-                return FALSE;
-            }
-            current = CacheSeg;
-
-            while (current != NULL)
-            {
-                Offset = WriteOffset.u.LowPart % VACB_MAPPING_GRANULARITY;
-                if ((Offset != 0) ||
-                    (Offset + CurrentLength < VACB_MAPPING_GRANULARITY))
-                {
-                    if (!current->Valid)
-                    {
-                        /* read the segment */
-                        Status = ReadCacheSegment(current);
-                        if (!NT_SUCCESS(Status))
-                        {
-                            DPRINT1("ReadCacheSegment 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 = 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;