https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2ae9feb59f8adbec05ca0ea9e556c1c990d8f7c2

commit 2ae9feb59f8adbec05ca0ea9e556c1c990d8f7c2
Author:     Jérôme Gardou <jerome.gar...@reactos.org>
AuthorDate: Mon Sep 5 19:33:19 2022 +0200
Commit:     Jérôme Gardou <zefk...@users.noreply.github.com>
CommitDate: Wed Nov 2 19:41:04 2022 +0100

    [NTOS] Properly implement and use FsRtlAcquireFileForModWriteEx
---
 ntoskrnl/fsrtl/fastio.c           | 203 +++++++++++++++++++-------------------
 ntoskrnl/include/internal/fsrtl.h |  12 +++
 ntoskrnl/mm/section.c             |  49 ++++++++-
 3 files changed, 160 insertions(+), 104 deletions(-)

diff --git a/ntoskrnl/fsrtl/fastio.c b/ntoskrnl/fsrtl/fastio.c
index ce629f77a02..8e19b2d77a9 100644
--- a/ntoskrnl/fsrtl/fastio.c
+++ b/ntoskrnl/fsrtl/fastio.c
@@ -1795,20 +1795,69 @@ FsRtlReleaseFileForCcFlush(IN PFILE_OBJECT FileObject)
     FsRtlExitFileSystem();
 }
 
-/*
-* @implemented
-*/
+/**
+ * @brief Get the resource to acquire when Mod Writer flushes data to disk
+ *
+ * @param FcbHeader - FCB header from the file object
+ * @param EndingOffset - The end offset of the write to be done
+ * @param ResourceToAcquire - Pointer receiving the resource to acquire before 
doing the write
+ *
+ * @return BOOLEAN specifying whether the resource must be acquired exclusively
+ */
+static
+BOOLEAN
+FsRtlpGetResourceForModWrite(_In_ PFSRTL_COMMON_FCB_HEADER FcbHeader,
+                             _In_ PLARGE_INTEGER EndingOffset,
+                             _Outptr_result_maybenull_ PERESOURCE* 
ResourceToAcquire)
+{
+    /*
+     * Decide on type of locking and type of resource based on
+     *  - Flags
+     *  - Whether we're extending ValidDataLength
+     */
+    if (FlagOn(FcbHeader->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX))
+    {
+        /* Acquire main resource, exclusive */
+        *ResourceToAcquire = FcbHeader->Resource;
+        return TRUE;
+    }
+
+    /* We will acquire shared. Which one ? */
+    if (FlagOn(FcbHeader->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH))
+    {
+        *ResourceToAcquire = FcbHeader->Resource;
+    }
+    else
+    {
+        *ResourceToAcquire = FcbHeader->PagingIoResource;
+    }
+
+    /* We force exclusive lock if this write modifies the valid data length */
+    return (EndingOffset->QuadPart > FcbHeader->ValidDataLength.QuadPart);
+}
+
+/**
+ * @brief Lock a file object before flushing pages to disk. 
+ *        To be called by the Modified Page Writer (MPW)
+ *
+ * @param FileObject - The file object to lock
+ * @param EndingOffset - The end offset of the write to be done
+ * @param ResourceToRelease - Pointer receiving the resource to release after 
the write
+ *
+ * @return Relevant NTSTATUS value
+ */
+_Check_return_
 NTSTATUS
 NTAPI
-FsRtlAcquireFileForModWriteEx(IN PFILE_OBJECT FileObject,
-                              IN PLARGE_INTEGER EndingOffset,
-                              IN PERESOURCE *ResourceToRelease)
+FsRtlAcquireFileForModWriteEx(_In_ PFILE_OBJECT FileObject,
+                              _In_ PLARGE_INTEGER EndingOffset,
+                              _Outptr_result_maybenull_ PERESOURCE 
*ResourceToRelease)
 {
     PFSRTL_COMMON_FCB_HEADER FcbHeader;
     PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
     PFAST_IO_DISPATCH FastDispatch;
     PERESOURCE ResourceToAcquire = NULL;
-    BOOLEAN Exclusive = FALSE;
+    BOOLEAN Exclusive;
     BOOLEAN Result;
     NTSTATUS Status = STATUS_SUCCESS;
 
@@ -1837,133 +1886,89 @@ FsRtlAcquireFileForModWriteEx(IN PFILE_OBJECT 
FileObject,
         }
     }
 
-    Status = STATUS_SUCCESS;
-
-    /* No FastIo handler, use algorithm from Nagar p.550. */
-    if (!FcbHeader->Resource)
-    {
-        *ResourceToRelease = NULL;
-        return STATUS_SUCCESS;
-    }
-
-    /* Default condition - shared acquiring of Paging IO Resource */
-    ResourceToAcquire = FcbHeader->PagingIoResource;
+    /* Check what and how we should acquire */
+    Exclusive = FsRtlpGetResourceForModWrite(FcbHeader, EndingOffset, 
&ResourceToAcquire);
 
-    /* Decide on type of locking and type of resource based on historical magic
-       well explain by Nagar in p. 550-551 */
-    if ((EndingOffset->QuadPart > FcbHeader->ValidDataLength.QuadPart &&
-         FcbHeader->FileSize.QuadPart != FcbHeader->ValidDataLength.QuadPart) 
||
-         (FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX))
-    {
-        /* Either exclusive flag is set or write operation is extending
-           the valid data length. Prefer exclusive acquire then */
-        Exclusive = TRUE;
-        ResourceToAcquire = FcbHeader->Resource;
-    }
-    else if (!FcbHeader->PagingIoResource ||
-             (FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH))
-    {
-        /* Acquire main resource shared if flag is specified or
-           if PagingIo resource is missing */
-        Exclusive = FALSE;
-        ResourceToAcquire = FcbHeader->Resource;
-    }
-
-    /* Acquire the resource in the loop, since the above code is unsafe */
+    /* Acquire the resource and loop until we're sure we got this right. */
     while (TRUE)
     {
-        Result = FALSE;
+        BOOLEAN OldExclusive;
+        PERESOURCE OldResourceToAcquire;
 
-        if (Exclusive)
-            Result = ExAcquireResourceExclusiveLite(ResourceToAcquire, FALSE);
-        else
-            Result = ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE);
+        if (ResourceToAcquire == NULL)
+        {
+            /* 
+             * There's nothing to acquire, we can simply return success
+             */
 
-        if (!Result) {
-            Status = STATUS_CANT_WAIT;
             break;
         }
 
-        /* Do the magic ifs again */
-        if ((EndingOffset->QuadPart > FcbHeader->ValidDataLength.QuadPart) ||
-             (FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX))
+        if (Exclusive)
         {
-            /* Check what we have */
-            if (Exclusive)
-            {
-                /* Asked for exclusive, got exclusive! */
-                break;
-            }
-            else
-            {
-                /* Asked for exclusive, got shared. Release it and retry. */
-                ExReleaseResourceLite(ResourceToAcquire);
-                Exclusive = TRUE;
-                ResourceToAcquire = FcbHeader->Resource;
-            }
+            Result = ExAcquireResourceExclusiveLite(ResourceToAcquire, FALSE);
         }
-        else if (FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH)
+        else
         {
-            if (Exclusive)
-            {
-                /* Asked for shared, got exclusive - convert */
-                ExConvertExclusiveToSharedLite(ResourceToAcquire);
-                break;
-            }
-            else if (ResourceToAcquire != FcbHeader->Resource)
-            {
-                /* Asked for main resource, got something else */
-                ExReleaseResourceLite(ResourceToAcquire);
-                ResourceToAcquire = FcbHeader->Resource;
-                Exclusive = TRUE;
-            }
+            Result = ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE);
         }
-        else if (FcbHeader->PagingIoResource &&
-                 ResourceToAcquire != FcbHeader->PagingIoResource)
+
+        if (!Result)
         {
-            /* There is PagingIo resource, but other resource was acquired */
-            ResourceToAcquire = FcbHeader->PagingIoResource;
-            if (!ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE))
-            {
-                Status = STATUS_CANT_WAIT;
-                ExReleaseResourceLite(FcbHeader->Resource);
-            }
+            return STATUS_CANT_WAIT;
+        }
+
+        /* Does this still hold true? */
+        OldExclusive = Exclusive;
+        OldResourceToAcquire = ResourceToAcquire;
+        Exclusive = FsRtlpGetResourceForModWrite(FcbHeader, EndingOffset, 
&ResourceToAcquire);
 
+        if ((OldExclusive == Exclusive) && (OldResourceToAcquire == 
ResourceToAcquire))
+        {
+            /* We're good */
             break;
         }
-        else if (Exclusive)
+
+        /* Can we fix this situation? */
+        if ((OldResourceToAcquire == ResourceToAcquire) && !Exclusive)
         {
-            /* Asked for shared got exclusive - convert */
+            /* We can easily do so */
             ExConvertExclusiveToSharedLite(ResourceToAcquire);
             break;
         }
-    }
 
-    /* If the resource was acquired successfully - pass it to the caller */
-    if (NT_SUCCESS(Status))
-        *ResourceToRelease = ResourceToAcquire;
+        /* Things have changed since we acquired the lock. Start again */
+        ExReleaseResourceLite(OldResourceToAcquire);
+    }
 
-    return Status;
+    /* If we're here, this means that we succeeded */
+    *ResourceToRelease = ResourceToAcquire;
+    return STATUS_SUCCESS;
 }
 
-/*
-* @implemented
-*/
+/**
+ * @brief Unlock a file object after flushing pages to disk. 
+ *        To be called by the Modified Page Writer (MPW) after a succesful 
call to
+ *        FsRtlAcquireFileForModWriteEx
+ *
+ * @param FileObject - The file object to unlock
+ * @param ResourceToRelease - The resource to release
+ */
 VOID
 NTAPI
-FsRtlReleaseFileForModWrite(IN PFILE_OBJECT FileObject,
-                            IN PERESOURCE ResourceToRelease)
+FsRtlReleaseFileForModWrite(_In_ PFILE_OBJECT FileObject,
+                            _In_ PERESOURCE ResourceToRelease)
 {
     PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
     PFAST_IO_DISPATCH FastDispatch;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status = STATUS_UNSUCCESSFUL;
 
     /* Get Device Object and Fast Calls */
     DeviceObject = IoGetRelatedDeviceObject(FileObject);
     BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
     FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
 
-    /* Check if Fast Calls are supported and check 
ReleaseFileForNtCreateSection */
+    /* Check if Fast Calls are supported and check ReleaseForModWrite */
     if (FastDispatch &&
         FastDispatch->ReleaseForModWrite)
     {
diff --git a/ntoskrnl/include/internal/fsrtl.h 
b/ntoskrnl/include/internal/fsrtl.h
index 5e106c38c1b..01425de1c56 100644
--- a/ntoskrnl/include/internal/fsrtl.h
+++ b/ntoskrnl/include/internal/fsrtl.h
@@ -159,3 +159,15 @@ FsRtlReleaseFileForCcFlush(IN PFILE_OBJECT FileObject);
 NTSTATUS
 NTAPI
 FsRtlAcquireFileForCcFlushEx(IN PFILE_OBJECT FileObject);
+
+_Check_return_
+NTSTATUS
+NTAPI
+FsRtlAcquireFileForModWriteEx(_In_ PFILE_OBJECT FileObject,
+                              _In_ PLARGE_INTEGER EndingOffset,
+                              _Outptr_result_maybenull_ PERESOURCE 
*ResourceToRelease);
+
+VOID
+NTAPI
+FsRtlReleaseFileForModWrite(IN PFILE_OBJECT FileObject,
+                            IN PERESOURCE ResourceToRelease);
diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c
index 21a9fc75560..61930738642 100644
--- a/ntoskrnl/mm/section.c
+++ b/ntoskrnl/mm/section.c
@@ -4965,17 +4965,56 @@ MmCheckDirtySegment(
 
         if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
         {
+            PERESOURCE ResourceToRelease = NULL;
+            KIRQL OldIrql;
+
             /* We have to write it back to the file. Tell the FS driver who we 
are */
             if (PageOut)
-                IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
+            {
+                LARGE_INTEGER EndOffset = *Offset;
 
-            /* Go ahead and write the page */
-            DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
-                    Offset->QuadPart, &Segment->FileObject->FileName, PageOut 
? "TRUE" : "FALSE");
-            Status = MiWritePage(Segment, Offset->QuadPart, Page);
+                ASSERT(IoGetTopLevelIrp() == NULL);
+
+                /* We need to disable all APCs */
+                KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+                EndOffset.QuadPart += PAGE_SIZE;
+                Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
+                                                       &EndOffset,
+                                                       &ResourceToRelease);
+                if (NT_SUCCESS(Status))
+                {
+                    IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
+                }
+                else
+                {
+                    /* Make sure we will not try to release anything */
+                    ResourceToRelease = NULL;
+                }
+            }
+            else
+            {
+                /* We don't have to lock. Say this is success */
+                Status = STATUS_SUCCESS;
+            }
+
+            /* Go ahead and write the page, if previous locking succeeded */
+            if (NT_SUCCESS(Status))
+            {
+                DPRINT("Writing page at offset %I64d for file %wZ, Pageout: 
%s\n",
+                        Offset->QuadPart, &Segment->FileObject->FileName, 
PageOut ? "TRUE" : "FALSE");
+                Status = MiWritePage(Segment, Offset->QuadPart, Page);
+            }
 
             if (PageOut)
+            {
                 IoSetTopLevelIrp(NULL);
+                if (ResourceToRelease != NULL)
+                {
+                    FsRtlReleaseFileForModWrite(Segment->FileObject, 
ResourceToRelease);
+                }
+                KeLowerIrql(OldIrql);
+            }
         }
         else
         {

Reply via email to