mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
[NTOS] Properly implement and use FsRtlAcquireFileForModWriteEx
This commit is contained in:
parent
36a7f0dc7c
commit
2ae9feb59f
3 changed files with 164 additions and 108 deletions
|
@ -1795,20 +1795,69 @@ FsRtlReleaseFileForCcFlush(IN PFILE_OBJECT FileObject)
|
||||||
FsRtlExitFileSystem();
|
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
|
NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
FsRtlAcquireFileForModWriteEx(IN PFILE_OBJECT FileObject,
|
FsRtlAcquireFileForModWriteEx(_In_ PFILE_OBJECT FileObject,
|
||||||
IN PLARGE_INTEGER EndingOffset,
|
_In_ PLARGE_INTEGER EndingOffset,
|
||||||
IN PERESOURCE *ResourceToRelease)
|
_Outptr_result_maybenull_ PERESOURCE *ResourceToRelease)
|
||||||
{
|
{
|
||||||
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
||||||
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
||||||
PFAST_IO_DISPATCH FastDispatch;
|
PFAST_IO_DISPATCH FastDispatch;
|
||||||
PERESOURCE ResourceToAcquire = NULL;
|
PERESOURCE ResourceToAcquire = NULL;
|
||||||
BOOLEAN Exclusive = FALSE;
|
BOOLEAN Exclusive;
|
||||||
BOOLEAN Result;
|
BOOLEAN Result;
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
@ -1837,133 +1886,89 @@ FsRtlAcquireFileForModWriteEx(IN PFILE_OBJECT FileObject,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Status = STATUS_SUCCESS;
|
/* Check what and how we should acquire */
|
||||||
|
Exclusive = FsRtlpGetResourceForModWrite(FcbHeader, EndingOffset, &ResourceToAcquire);
|
||||||
|
|
||||||
/* No FastIo handler, use algorithm from Nagar p.550. */
|
/* Acquire the resource and loop until we're sure we got this right. */
|
||||||
if (!FcbHeader->Resource)
|
|
||||||
{
|
|
||||||
*ResourceToRelease = NULL;
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default condition - shared acquiring of Paging IO Resource */
|
|
||||||
ResourceToAcquire = FcbHeader->PagingIoResource;
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
Result = FALSE;
|
BOOLEAN OldExclusive;
|
||||||
|
PERESOURCE OldResourceToAcquire;
|
||||||
|
|
||||||
|
if (ResourceToAcquire == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There's nothing to acquire, we can simply return success
|
||||||
|
*/
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (Exclusive)
|
if (Exclusive)
|
||||||
|
{
|
||||||
Result = ExAcquireResourceExclusiveLite(ResourceToAcquire, FALSE);
|
Result = ExAcquireResourceExclusiveLite(ResourceToAcquire, FALSE);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
Result = ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE);
|
Result = ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Result) {
|
if (!Result)
|
||||||
Status = STATUS_CANT_WAIT;
|
{
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do the magic ifs again */
|
/* Can we fix this situation? */
|
||||||
if ((EndingOffset->QuadPart > FcbHeader->ValidDataLength.QuadPart) ||
|
if ((OldResourceToAcquire == ResourceToAcquire) && !Exclusive)
|
||||||
(FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX))
|
|
||||||
{
|
{
|
||||||
/* Check what we have */
|
/* We can easily do so */
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (FcbHeader->Flags & FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (FcbHeader->PagingIoResource &&
|
|
||||||
ResourceToAcquire != FcbHeader->PagingIoResource)
|
|
||||||
{
|
|
||||||
/* There is PagingIo resource, but other resource was acquired */
|
|
||||||
ResourceToAcquire = FcbHeader->PagingIoResource;
|
|
||||||
if (!ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE))
|
|
||||||
{
|
|
||||||
Status = STATUS_CANT_WAIT;
|
|
||||||
ExReleaseResourceLite(FcbHeader->Resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (Exclusive)
|
|
||||||
{
|
|
||||||
/* Asked for shared got exclusive - convert */
|
|
||||||
ExConvertExclusiveToSharedLite(ResourceToAcquire);
|
ExConvertExclusiveToSharedLite(ResourceToAcquire);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Things have changed since we acquired the lock. Start again */
|
||||||
|
ExReleaseResourceLite(OldResourceToAcquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the resource was acquired successfully - pass it to the caller */
|
/* If we're here, this means that we succeeded */
|
||||||
if (NT_SUCCESS(Status))
|
*ResourceToRelease = ResourceToAcquire;
|
||||||
*ResourceToRelease = ResourceToAcquire;
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
return Status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* @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
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
FsRtlReleaseFileForModWrite(IN PFILE_OBJECT FileObject,
|
FsRtlReleaseFileForModWrite(_In_ PFILE_OBJECT FileObject,
|
||||||
IN PERESOURCE ResourceToRelease)
|
_In_ PERESOURCE ResourceToRelease)
|
||||||
{
|
{
|
||||||
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
||||||
PFAST_IO_DISPATCH FastDispatch;
|
PFAST_IO_DISPATCH FastDispatch;
|
||||||
NTSTATUS Status = STATUS_SUCCESS;
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||||||
|
|
||||||
/* Get Device Object and Fast Calls */
|
/* Get Device Object and Fast Calls */
|
||||||
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
||||||
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
||||||
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
||||||
|
|
||||||
/* Check if Fast Calls are supported and check ReleaseFileForNtCreateSection */
|
/* Check if Fast Calls are supported and check ReleaseForModWrite */
|
||||||
if (FastDispatch &&
|
if (FastDispatch &&
|
||||||
FastDispatch->ReleaseForModWrite)
|
FastDispatch->ReleaseForModWrite)
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,3 +159,15 @@ FsRtlReleaseFileForCcFlush(IN PFILE_OBJECT FileObject);
|
||||||
NTSTATUS
|
NTSTATUS
|
||||||
NTAPI
|
NTAPI
|
||||||
FsRtlAcquireFileForCcFlushEx(IN PFILE_OBJECT FileObject);
|
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);
|
||||||
|
|
|
@ -4965,17 +4965,56 @@ MmCheckDirtySegment(
|
||||||
|
|
||||||
if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
|
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 */
|
/* We have to write it back to the file. Tell the FS driver who we are */
|
||||||
if (PageOut)
|
if (PageOut)
|
||||||
IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
|
{
|
||||||
|
LARGE_INTEGER EndOffset = *Offset;
|
||||||
|
|
||||||
/* Go ahead and write the page */
|
ASSERT(IoGetTopLevelIrp() == NULL);
|
||||||
DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
|
|
||||||
Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
|
/* We need to disable all APCs */
|
||||||
Status = MiWritePage(Segment, Offset->QuadPart, Page);
|
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)
|
if (PageOut)
|
||||||
|
{
|
||||||
IoSetTopLevelIrp(NULL);
|
IoSetTopLevelIrp(NULL);
|
||||||
|
if (ResourceToRelease != NULL)
|
||||||
|
{
|
||||||
|
FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
|
||||||
|
}
|
||||||
|
KeLowerIrql(OldIrql);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue