mirror of
https://github.com/reactos/reactos.git
synced 2024-12-26 17:14:41 +00:00
8ed8682256
Don't return before file object's resource is acquired in FsRtlAcquireFileExclusiveCommon, except some special return cases, when return is required. Based on hpoussin_filter_extra.patch by Herve Poussineau (@hpoussin) with improved comment, which matches the actual behaviour now. This is required by fltmgr.sys driver from Windows XP/Server 2003 to work correctly, so this change fixes asserts/exceptions when releasing the file via FsRtlReleaseFile after acquiring, when using 3rd party filter drivers from several antivirus programs (e. g., Avast Free Antivirus all versions, AVG Antivirus Free 18.8, Avira AntiVir Personal 8.2, Dr. Web Security Space 8.0, Kaspersky Antivirus 2012 etc. etc.). CORE-14157, CORE-14635, CORE-19318
2045 lines
65 KiB
C
2045 lines
65 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/fsrtl/fastio.c
|
|
* PURPOSE: Provides Fast I/O entrypoints to the Cache Manager
|
|
* PROGRAMMERS: Dominique Cote (buzdelabuz2@gmail.com)
|
|
* Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Aleksey Bragin (aleksey@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlIncrementCcFastReadResourceMiss(VOID)
|
|
{
|
|
CcFastReadResourceMiss++;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlIncrementCcFastReadNotPossible(VOID)
|
|
{
|
|
CcFastReadNotPossible++;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlIncrementCcFastReadWait(VOID)
|
|
{
|
|
CcFastReadWait++;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlIncrementCcFastReadNoWait(VOID)
|
|
{
|
|
CcFastReadNoWait++;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlCopyRead(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
LARGE_INTEGER Offset;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PDEVICE_OBJECT Device;
|
|
BOOLEAN Result = TRUE;
|
|
ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(FileOffset, Length);
|
|
|
|
PAGED_CODE();
|
|
ASSERT(FileObject);
|
|
ASSERT(FileObject->FsContext);
|
|
|
|
/* No actual read */
|
|
if (!Length)
|
|
{
|
|
/* Return success */
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (Length > MAXLONGLONG - FileOffset->QuadPart)
|
|
{
|
|
IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
IoStatus->Information = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the offset and FCB header */
|
|
Offset.QuadPart = FileOffset->QuadPart + Length;
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
if (Wait)
|
|
{
|
|
/* Use a Resource Acquire */
|
|
FsRtlEnterFileSystem();
|
|
CcFastReadWait++;
|
|
ExAcquireResourceSharedLite(FcbHeader->Resource, TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* Acquire the resource without blocking. Return false and the I/O manager
|
|
* will retry using the standard IRP method. Use a Resource Acquire.
|
|
*/
|
|
FsRtlEnterFileSystem();
|
|
if (!ExAcquireResourceSharedLite(FcbHeader->Resource, FALSE))
|
|
{
|
|
FsRtlExitFileSystem();
|
|
FsRtlIncrementCcFastReadResourceMiss();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check if this is a fast I/O cached file */
|
|
if (!(FileObject->PrivateCacheMap) ||
|
|
(FcbHeader->IsFastIoPossible == FastIoIsNotPossible))
|
|
{
|
|
/* It's not, so fail */
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check if we need to find out if fast I/O is available */
|
|
if (FcbHeader->IsFastIoPossible == FastIoIsQuestionable)
|
|
{
|
|
/* Sanity check */
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
/* Get the Fast I/O table */
|
|
Device = IoGetRelatedDeviceObject(FileObject);
|
|
FastIoDispatch = Device->DriverObject->FastIoDispatch;
|
|
|
|
/* Sanity check */
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
/* Ask the driver if we can do it */
|
|
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
TRUE,
|
|
IoStatus,
|
|
Device))
|
|
{
|
|
/* It's not, fail */
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/* Check if we read too much */
|
|
if (Offset.QuadPart > FcbHeader->FileSize.QuadPart)
|
|
{
|
|
/* We did, check if the file offset is past the end */
|
|
if (FileOffset->QuadPart >= FcbHeader->FileSize.QuadPart)
|
|
{
|
|
/* Set end of file */
|
|
IoStatus->Status = STATUS_END_OF_FILE;
|
|
IoStatus->Information = 0;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Otherwise, just normalize the length */
|
|
Length = (ULONG)(FcbHeader->FileSize.QuadPart - FileOffset->QuadPart);
|
|
}
|
|
|
|
/* Set this as top-level IRP */
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Make sure the IO and file size is below 4GB */
|
|
if (Wait && !(Offset.HighPart | FcbHeader->FileSize.HighPart))
|
|
{
|
|
|
|
/* Call the cache controller */
|
|
CcFastCopyRead(FileObject,
|
|
FileOffset->LowPart,
|
|
Length,
|
|
PageCount,
|
|
Buffer,
|
|
IoStatus);
|
|
|
|
/* File was accessed */
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
|
|
if (IoStatus->Status != STATUS_END_OF_FILE)
|
|
{
|
|
ASSERT((ULONGLONG)FcbHeader->FileSize.QuadPart >=
|
|
((ULONGLONG)FileOffset->QuadPart + IoStatus->Information));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Call the cache controller */
|
|
Result = CcCopyRead(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
Buffer,
|
|
IoStatus);
|
|
|
|
/* File was accessed */
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
|
|
if (Result != FALSE)
|
|
{
|
|
ASSERT((IoStatus->Status == STATUS_END_OF_FILE) ||
|
|
(((ULONGLONG)FileOffset->QuadPart + IoStatus->Information) <=
|
|
(ULONGLONG)FcbHeader->FileSize.QuadPart));
|
|
}
|
|
}
|
|
|
|
/* Update the current file offset */
|
|
if (Result != FALSE)
|
|
{
|
|
FileObject->CurrentByteOffset.QuadPart = FileOffset->QuadPart + IoStatus->Information;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
/* Return to caller */
|
|
Cleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
|
|
if (Result == FALSE)
|
|
{
|
|
CcFastReadNotPossible += 1;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlCopyWrite(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN Result = TRUE;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PDEVICE_OBJECT Device;
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PSHARED_CACHE_MAP SharedCacheMap;
|
|
|
|
/* WDK doc.
|
|
* Offset == 0xffffffffffffffff indicates append to the end of file.
|
|
*/
|
|
BOOLEAN FileOffsetAppend = (FileOffset->HighPart == (LONG)0xffffffff) &&
|
|
(FileOffset->LowPart == 0xffffffff);
|
|
|
|
BOOLEAN ResourceAcquiredShared = FALSE;
|
|
BOOLEAN b_4GB = FALSE;
|
|
BOOLEAN FileSizeModified = FALSE;
|
|
LARGE_INTEGER OldFileSize;
|
|
LARGE_INTEGER OldValidDataLength;
|
|
LARGE_INTEGER NewSize;
|
|
LARGE_INTEGER Offset;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(FileObject);
|
|
ASSERT(FileObject->FsContext);
|
|
|
|
/* Initialize some of the vars and pointers */
|
|
NewSize.QuadPart = 0;
|
|
Offset.QuadPart = FileOffset->QuadPart + Length;
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
/* Nagar p.544.
|
|
* Check with Cc if we can write and check if the IO > 64kB (WDK macro).
|
|
*/
|
|
if ((CcCanIWrite(FileObject, Length, Wait, FALSE) == FALSE) ||
|
|
(CcCopyWriteWontFlush(FileObject, FileOffset, Length) == FALSE) ||
|
|
((FileObject->Flags & FO_WRITE_THROUGH)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Already init IO_STATUS_BLOCK */
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = Length;
|
|
|
|
/* No actual read */
|
|
if (!Length)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
/* Nagar p.544/545.
|
|
* The CcFastCopyWrite doesn't deal with filesize beyond 4GB.
|
|
*/
|
|
if (Wait && (FcbHeader->AllocationSize.HighPart == 0))
|
|
{
|
|
/* If the file offset is not past the file size,
|
|
* then we can acquire the lock shared.
|
|
*/
|
|
if ((FileOffsetAppend == FALSE) &&
|
|
(Offset.LowPart <= FcbHeader->ValidDataLength.LowPart))
|
|
{
|
|
ExAcquireResourceSharedLite(FcbHeader->Resource, TRUE);
|
|
ResourceAcquiredShared = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
}
|
|
|
|
/* Nagar p.544/545.
|
|
* If we append, use the file size as offset.
|
|
* Also, check that we aren't crossing the 4GB boundary.
|
|
*/
|
|
if (FileOffsetAppend != FALSE)
|
|
{
|
|
Offset.LowPart = FcbHeader->FileSize.LowPart;
|
|
NewSize.LowPart = FcbHeader->FileSize.LowPart + Length;
|
|
b_4GB = (NewSize.LowPart < FcbHeader->FileSize.LowPart);
|
|
|
|
}
|
|
else
|
|
{
|
|
Offset.LowPart = FileOffset->LowPart;
|
|
NewSize.LowPart = FileOffset->LowPart + Length;
|
|
b_4GB = (NewSize.LowPart < FileOffset->LowPart) ||
|
|
(FileOffset->HighPart != 0);
|
|
}
|
|
|
|
/* Nagar p.544/545.
|
|
* Make sure that caching is initated.
|
|
* That fast are allowed for this file stream.
|
|
* That we are not extending past the allocated size.
|
|
* That we are not creating a hole bigger than 8k.
|
|
* That we are not crossing the 4GB boundary.
|
|
*/
|
|
if ((FileObject->PrivateCacheMap != NULL) &&
|
|
(FcbHeader->IsFastIoPossible != FastIoIsNotPossible) &&
|
|
(FcbHeader->AllocationSize.LowPart >= NewSize.LowPart) &&
|
|
(Offset.LowPart < FcbHeader->ValidDataLength.LowPart + 0x2000) &&
|
|
!b_4GB)
|
|
{
|
|
/* If we are extending past the file size, we need to
|
|
* release the lock and acquire it exclusively, because
|
|
* we are going to need to update the FcbHeader.
|
|
*/
|
|
if (ResourceAcquiredShared &&
|
|
(NewSize.LowPart > FcbHeader->ValidDataLength.LowPart + 0x2000))
|
|
{
|
|
/* Then we need to acquire the resource exclusive */
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
if (FileOffsetAppend != FALSE)
|
|
{
|
|
Offset.LowPart = FcbHeader->FileSize.LowPart; // ??
|
|
NewSize.LowPart = FcbHeader->FileSize.LowPart + Length;
|
|
|
|
/* Make sure we don't cross the 4GB boundary */
|
|
b_4GB = (NewSize.LowPart < Offset.LowPart);
|
|
}
|
|
|
|
/* Recheck some of the conditions since we let the lock go */
|
|
if ((FileObject->PrivateCacheMap != NULL) &&
|
|
(FcbHeader->IsFastIoPossible != FastIoIsNotPossible) &&
|
|
(FcbHeader->AllocationSize.LowPart >= NewSize.LowPart) &&
|
|
(FcbHeader->AllocationSize.HighPart == 0) &&
|
|
!b_4GB)
|
|
{
|
|
/* Do nothing? */
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
|
|
/* Check if we need to find out if fast I/O is available */
|
|
if (FcbHeader->IsFastIoPossible == FastIoIsQuestionable)
|
|
{
|
|
IO_STATUS_BLOCK FastIoCheckIfPossibleStatus;
|
|
|
|
/* Sanity check */
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
/* Get the Fast I/O table */
|
|
Device = IoGetRelatedDeviceObject(FileObject);
|
|
FastIoDispatch = Device->DriverObject->FastIoDispatch;
|
|
|
|
/* Sanity check */
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
/* Ask the driver if we can do it */
|
|
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
|
|
FileOffsetAppend ?
|
|
&FcbHeader->FileSize :
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
FALSE,
|
|
&FastIoCheckIfPossibleStatus,
|
|
Device))
|
|
{
|
|
/* It's not, fail */
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
/* If we are going to extend the file then save
|
|
* the old file size in case the operation fails.
|
|
*/
|
|
if (NewSize.LowPart > FcbHeader->FileSize.LowPart)
|
|
{
|
|
FileSizeModified = TRUE;
|
|
OldFileSize.LowPart = FcbHeader->FileSize.LowPart;
|
|
OldValidDataLength.LowPart = FcbHeader->ValidDataLength.LowPart;
|
|
FcbHeader->FileSize.LowPart = NewSize.LowPart;
|
|
}
|
|
|
|
/* Set this as top-level IRP */
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (Offset.LowPart > FcbHeader->ValidDataLength.LowPart)
|
|
{
|
|
LARGE_INTEGER OffsetVar;
|
|
OffsetVar.LowPart = Offset.LowPart;
|
|
OffsetVar.HighPart = 0;
|
|
CcZeroData(FileObject, &FcbHeader->ValidDataLength, &OffsetVar, TRUE);
|
|
}
|
|
|
|
/* Call the cache manager */
|
|
CcFastCopyWrite(FileObject, Offset.LowPart, Length, Buffer);
|
|
}
|
|
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Remove ourselves at the top level component after the IO is done */
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
/* Did the operation succeed? */
|
|
if (Result != FALSE)
|
|
{
|
|
/* Update the valid file size if necessary */
|
|
if (NewSize.LowPart > FcbHeader->ValidDataLength.LowPart)
|
|
{
|
|
FcbHeader->ValidDataLength.LowPart = NewSize.LowPart;
|
|
}
|
|
|
|
/* Flag the file as modified */
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
/* Update the strucutres if the file size changed */
|
|
if (FileSizeModified)
|
|
{
|
|
SharedCacheMap =
|
|
(PSHARED_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap;
|
|
SharedCacheMap->FileSize.LowPart = NewSize.LowPart;
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
|
|
/* Update the file object current file offset */
|
|
FileObject->CurrentByteOffset.QuadPart = NewSize.LowPart;
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Result == FALSE if we get here */
|
|
if (FileSizeModified)
|
|
{
|
|
/* If the file size was modified then restore the old file size */
|
|
if (FcbHeader->PagingIoResource != NULL)
|
|
{
|
|
/* Nagar P.544.
|
|
* Restore the old file size if operation didn't succeed.
|
|
*/
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->FileSize.LowPart = OldFileSize.LowPart;
|
|
FcbHeader->ValidDataLength.LowPart = OldValidDataLength.LowPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
/* If there is no lock and do it without */
|
|
FcbHeader->FileSize.LowPart = OldFileSize.LowPart;
|
|
FcbHeader->ValidDataLength.LowPart = OldValidDataLength.LowPart;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Do nothing? */
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER OldFileSize;
|
|
|
|
/* Sanity check */
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
/* Nagar P.544.
|
|
* Check if we need to acquire the resource exclusive.
|
|
*/
|
|
if ((FileOffsetAppend == FALSE) &&
|
|
(FileOffset->QuadPart + Length <= FcbHeader->ValidDataLength.QuadPart))
|
|
{
|
|
/* Acquire the resource shared */
|
|
if (!ExAcquireResourceSharedLite(FcbHeader->Resource, Wait))
|
|
{
|
|
goto LeaveCriticalAndFail;
|
|
}
|
|
ResourceAcquiredShared = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Acquire the resource exclusive */
|
|
if (!ExAcquireResourceExclusiveLite(FcbHeader->Resource, Wait))
|
|
{
|
|
goto LeaveCriticalAndFail;
|
|
}
|
|
}
|
|
|
|
/* Check if we are appending */
|
|
if (FileOffsetAppend != FALSE)
|
|
{
|
|
Offset.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
NewSize.QuadPart = FcbHeader->FileSize.QuadPart + Length;
|
|
}
|
|
else
|
|
{
|
|
Offset.QuadPart = FileOffset->QuadPart;
|
|
NewSize.QuadPart += FileOffset->QuadPart + Length;
|
|
}
|
|
|
|
/* Nagar p.544/545.
|
|
* Make sure that caching is initated.
|
|
* That fast are allowed for this file stream.
|
|
* That we are not extending past the allocated size.
|
|
* That we are not creating a hole bigger than 8k.
|
|
*/
|
|
if ((FileObject->PrivateCacheMap != NULL) &&
|
|
(FcbHeader->IsFastIoPossible != FastIoIsNotPossible) &&
|
|
(FcbHeader->ValidDataLength.QuadPart + 0x2000 > Offset.QuadPart) &&
|
|
(Length <= MAXLONGLONG - Offset.QuadPart) &&
|
|
(FcbHeader->AllocationSize.QuadPart >= NewSize.QuadPart))
|
|
{
|
|
/* Check if we can keep the lock shared */
|
|
if (ResourceAcquiredShared &&
|
|
(NewSize.QuadPart > FcbHeader->ValidDataLength.QuadPart))
|
|
{
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
if (!ExAcquireResourceExclusiveLite(FcbHeader->Resource, Wait))
|
|
{
|
|
goto LeaveCriticalAndFail;
|
|
}
|
|
|
|
/* Compute the offset and the new filesize */
|
|
if (FileOffsetAppend)
|
|
{
|
|
Offset.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
NewSize.QuadPart = FcbHeader->FileSize.QuadPart + Length;
|
|
}
|
|
|
|
/* Recheck the above points since we released and reacquire the lock */
|
|
if ((FileObject->PrivateCacheMap != NULL) &&
|
|
(FcbHeader->IsFastIoPossible != FastIoIsNotPossible) &&
|
|
(FcbHeader->AllocationSize.QuadPart > NewSize.QuadPart))
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
/* Check if we need to find out if fast I/O is available */
|
|
if (FcbHeader->IsFastIoPossible == FastIoIsQuestionable)
|
|
{
|
|
IO_STATUS_BLOCK FastIoCheckIfPossibleStatus;
|
|
|
|
/* Sanity check */
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
/* Get the Fast I/O table */
|
|
Device = IoGetRelatedDeviceObject(FileObject);
|
|
FastIoDispatch = Device->DriverObject->FastIoDispatch;
|
|
|
|
/* Sanity check */
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
/* Ask the driver if we can do it */
|
|
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
|
|
FileOffsetAppend ?
|
|
&FcbHeader->FileSize :
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
LockKey,
|
|
FALSE,
|
|
&FastIoCheckIfPossibleStatus,
|
|
Device))
|
|
{
|
|
/* It's not, fail */
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
/* If we are going to modify the filesize,
|
|
* save the old fs in case the operation fails.
|
|
*/
|
|
if (NewSize.QuadPart > FcbHeader->FileSize.QuadPart)
|
|
{
|
|
FileSizeModified = TRUE;
|
|
OldFileSize.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
OldValidDataLength.QuadPart = FcbHeader->ValidDataLength.QuadPart;
|
|
|
|
/* If the high part of the filesize is going
|
|
* to change, grab the Paging IoResouce.
|
|
*/
|
|
if (NewSize.HighPart != FcbHeader->FileSize.HighPart &&
|
|
FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->FileSize.QuadPart = NewSize.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->FileSize.QuadPart = NewSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
/* Nagar p.544.
|
|
* Set ourselves as top component.
|
|
*/
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
BOOLEAN CallCc = TRUE;
|
|
|
|
/* Check if there is a gap between the end of the file
|
|
* and the offset. If yes, then we have to zero the data.
|
|
*/
|
|
if (Offset.QuadPart > FcbHeader->ValidDataLength.QuadPart)
|
|
{
|
|
if (!(Result = CcZeroData(FileObject,
|
|
&FcbHeader->ValidDataLength,
|
|
&Offset,
|
|
Wait)))
|
|
{
|
|
/* If this operation fails, then we have to exit. We can't jump
|
|
* outside the SEH, so I am using a variable to exit normally.
|
|
*/
|
|
CallCc = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Unless the CcZeroData failed, call the cache manager */
|
|
if (CallCc)
|
|
{
|
|
Result = CcCopyWrite(FileObject, &Offset, Length, Wait, Buffer);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reset the top component */
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
/* Did the operation suceeded */
|
|
if (Result)
|
|
{
|
|
/* Check if we need to update the filesize */
|
|
if (NewSize.QuadPart > FcbHeader->ValidDataLength.QuadPart)
|
|
{
|
|
if (NewSize.HighPart != FcbHeader->ValidDataLength.HighPart &&
|
|
FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->ValidDataLength.QuadPart = NewSize.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->ValidDataLength.QuadPart = NewSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
/* Flag the file as modified */
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
/* Check if the filesize has changed */
|
|
if (FileSizeModified)
|
|
{
|
|
/* Update the file sizes */
|
|
SharedCacheMap =
|
|
(PSHARED_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap;
|
|
SharedCacheMap->FileSize.QuadPart = NewSize.QuadPart;
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
|
|
/* Update the current FO byte offset */
|
|
FileObject->CurrentByteOffset.QuadPart = NewSize.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
/* The operation did not succeed.
|
|
* Reset the file size to what it should be.
|
|
*/
|
|
if (FileSizeModified)
|
|
{
|
|
if (FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->FileSize.QuadPart = OldFileSize.QuadPart;
|
|
FcbHeader->ValidDataLength.QuadPart = OldValidDataLength.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->FileSize.QuadPart = OldFileSize.QuadPart;
|
|
FcbHeader->ValidDataLength.QuadPart = OldValidDataLength.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalAndFail:
|
|
|
|
FsRtlExitFileSystem();
|
|
return FALSE;
|
|
|
|
FailAndCleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
return FALSE;
|
|
|
|
Cleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
return Result;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlGetFileSize(IN PFILE_OBJECT FileObject,
|
|
IN OUT PLARGE_INTEGER FileSize)
|
|
{
|
|
FILE_STANDARD_INFORMATION Info;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
KEVENT Event;
|
|
PIO_STACK_LOCATION IoStackLocation;
|
|
PIRP Irp;
|
|
BOOLEAN OldHardError;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if we support Fast Calls, and check FastIoQueryStandardInfo.
|
|
* Call the function and see if it succeeds.
|
|
*/
|
|
if (!FastDispatch ||
|
|
!FastDispatch->FastIoQueryStandardInfo ||
|
|
!FastDispatch->FastIoQueryStandardInfo(FileObject,
|
|
TRUE,
|
|
&Info,
|
|
&IoStatus,
|
|
DeviceObject))
|
|
{
|
|
/* If any of the above failed, then we are going to send an
|
|
* IRP to the device object. Initialize the event for the IO.
|
|
*/
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
|
|
if (Irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Don't process hard error */
|
|
OldHardError = IoSetThreadHardErrorMode(FALSE);
|
|
|
|
/* Setup the IRP */
|
|
Irp->UserIosb = &IoStatus;
|
|
Irp->UserEvent = &Event;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Flags = IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->AssociatedIrp.SystemBuffer = &Info;
|
|
|
|
/* Setup out stack location */
|
|
IoStackLocation = Irp->Tail.Overlay.CurrentStackLocation;
|
|
IoStackLocation--;
|
|
IoStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
|
|
IoStackLocation->FileObject = FileObject;
|
|
IoStackLocation->DeviceObject = DeviceObject;
|
|
IoStackLocation->Parameters.QueryFile.Length =
|
|
sizeof(FILE_STANDARD_INFORMATION);
|
|
IoStackLocation->Parameters.QueryFile.FileInformationClass =
|
|
FileStandardInformation;
|
|
|
|
/* Send the IRP to the related device object */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
/* Standard DDK IRP result processing */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
/* If there was a synchronous error, signal it */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
IoStatus.Status = Status;
|
|
}
|
|
|
|
IoSetThreadHardErrorMode(OldHardError);
|
|
}
|
|
|
|
/* Check the sync/async IO result */
|
|
if (NT_SUCCESS(IoStatus.Status))
|
|
{
|
|
/* Was the request for a directory? */
|
|
if (Info.Directory)
|
|
{
|
|
IoStatus.Status = STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
else
|
|
{
|
|
FileSize->QuadPart = Info.EndOfFile.QuadPart;
|
|
}
|
|
}
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlRead(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if we support Fast Calls, and check this one */
|
|
if (FastDispatch && FastDispatch->MdlRead)
|
|
{
|
|
/* Use the fast path */
|
|
return FastDispatch->MdlRead(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
LockKey,
|
|
MdlChain,
|
|
IoStatus,
|
|
DeviceObject);
|
|
}
|
|
|
|
/* Get the Base File System (Volume) and Fast Calls */
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = BaseDeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* If the Base Device Object has its own FastDispatch Routine, fail */
|
|
if (FastDispatch && FastDispatch->MdlRead && BaseDeviceObject != DeviceObject)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* No fast path, use slow path */
|
|
return FsRtlMdlReadDev(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
LockKey,
|
|
MdlChain,
|
|
IoStatus,
|
|
DeviceObject);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlReadComplete(IN PFILE_OBJECT FileObject,
|
|
IN OUT PMDL MdlChain)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if we support Fast Calls, and check this one */
|
|
if (FastDispatch && FastDispatch->MdlReadComplete)
|
|
{
|
|
/* Use the fast path */
|
|
return FastDispatch->MdlReadComplete(FileObject, MdlChain, DeviceObject);
|
|
}
|
|
|
|
/* Get the Base File System (Volume) and Fast Calls */
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = BaseDeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* If the Base Device Object has its own FastDispatch Routine, fail */
|
|
if ((BaseDeviceObject != DeviceObject) &&
|
|
FastDispatch &&
|
|
FastDispatch->MdlReadComplete)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* No fast path, use slow path */
|
|
return FsRtlMdlReadCompleteDev(FileObject, MdlChain, DeviceObject);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlReadCompleteDev(IN PFILE_OBJECT FileObject,
|
|
IN PMDL MemoryDescriptorList,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
/* Call the Cache Manager */
|
|
CcMdlReadComplete2(FileObject, MemoryDescriptorList);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlReadDev(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
BOOLEAN Result = TRUE;
|
|
LARGE_INTEGER Offset;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PDEVICE_OBJECT Device;
|
|
PAGED_CODE();
|
|
|
|
/* No actual read */
|
|
if (!Length)
|
|
{
|
|
/* Return success */
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Sanity check */
|
|
ASSERT(MAXLONGLONG - FileOffset->QuadPart >= (LONGLONG)Length);
|
|
|
|
/* Get the offset and FCB header */
|
|
Offset.QuadPart = FileOffset->QuadPart + Length;
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
/* Enter the FS */
|
|
FsRtlEnterFileSystem();
|
|
CcFastMdlReadWait++;
|
|
|
|
/* Lock the FCB */
|
|
ExAcquireResourceShared(FcbHeader->Resource, TRUE);
|
|
|
|
/* Check if this is a fast I/O cached file */
|
|
if (!(FileObject->PrivateCacheMap) ||
|
|
(FcbHeader->IsFastIoPossible == FastIoIsNotPossible))
|
|
{
|
|
/* It's not, so fail */
|
|
CcFastMdlReadNotPossible += 1;
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check if we need to find out if fast I/O is available */
|
|
if (FcbHeader->IsFastIoPossible == FastIoIsQuestionable)
|
|
{
|
|
/* Get the Fast I/O table */
|
|
Device = IoGetRelatedDeviceObject(FileObject);
|
|
FastIoDispatch = Device->DriverObject->FastIoDispatch;
|
|
|
|
/* Sanity check */
|
|
ASSERT(!KeIsExecutingDpc());
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
/* Ask the driver if we can do it */
|
|
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
TRUE,
|
|
IoStatus,
|
|
Device))
|
|
{
|
|
/* It's not, fail */
|
|
CcFastMdlReadNotPossible += 1;
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/* Check if we read too much */
|
|
if (Offset.QuadPart > FcbHeader->FileSize.QuadPart)
|
|
{
|
|
/* We did, check if the file offset is past the end */
|
|
if (FileOffset->QuadPart >= FcbHeader->FileSize.QuadPart)
|
|
{
|
|
/* Set end of file */
|
|
IoStatus->Status = STATUS_END_OF_FILE;
|
|
IoStatus->Information = 0;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Otherwise, just normalize the length */
|
|
Length = (ULONG)(FcbHeader->FileSize.QuadPart - FileOffset->QuadPart);
|
|
}
|
|
|
|
/* Set this as top-level IRP */
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Attempt a read */
|
|
CcMdlRead(FileObject, FileOffset, Length, MdlChain, IoStatus);
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
}
|
|
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
|
|
/* Remove the top-level IRP flag */
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
/* Return to caller */
|
|
Cleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
|
|
return Result;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlWriteComplete(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN PMDL MdlChain)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if we support Fast Calls, and check this one */
|
|
if (FastDispatch && FastDispatch->MdlWriteComplete)
|
|
{
|
|
/* Use the fast path */
|
|
return FastDispatch->MdlWriteComplete(FileObject,
|
|
FileOffset,
|
|
MdlChain,
|
|
DeviceObject);
|
|
}
|
|
|
|
/* Get the Base File System (Volume) and Fast Calls */
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = BaseDeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* If the Base Device Object has its own FastDispatch Routine, fail */
|
|
if (FastDispatch &&
|
|
FastDispatch->MdlWriteComplete &&
|
|
BaseDeviceObject != DeviceObject)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* No fast path, use slow path */
|
|
return FsRtlMdlWriteCompleteDev(FileObject,
|
|
FileOffset,
|
|
MdlChain,
|
|
DeviceObject);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlMdlWriteCompleteDev(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN PMDL MdlChain,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
if (FileObject->Flags & FO_WRITE_THROUGH)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Call the Cache Manager */
|
|
CcMdlWriteComplete2(FileObject, FileOffset, MdlChain);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlPrepareMdlWrite(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if we support Fast Calls, and check this one */
|
|
if (FastDispatch && FastDispatch->PrepareMdlWrite)
|
|
{
|
|
/* Use the fast path */
|
|
return FastDispatch->PrepareMdlWrite(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
LockKey,
|
|
MdlChain,
|
|
IoStatus,
|
|
DeviceObject);
|
|
}
|
|
|
|
/* Get the Base File System (Volume) and Fast Calls */
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = BaseDeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* If the Base Device Object has its own FastDispatch Routine, fail */
|
|
if (FastDispatch &&
|
|
FastDispatch->PrepareMdlWrite &&
|
|
BaseDeviceObject != DeviceObject)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* No fast path, use slow path */
|
|
return FsRtlPrepareMdlWriteDev(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
LockKey,
|
|
MdlChain,
|
|
IoStatus,
|
|
DeviceObject);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
FsRtlPrepareMdlWriteDev(IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
BOOLEAN Result = TRUE;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PDEVICE_OBJECT Device;
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PSHARED_CACHE_MAP SharedCacheMap;
|
|
|
|
LARGE_INTEGER OldFileSize;
|
|
LARGE_INTEGER OldValidDataLength;
|
|
LARGE_INTEGER NewSize;
|
|
LARGE_INTEGER Offset;
|
|
|
|
/* WDK doc.
|
|
* Offset == 0xffffffffffffffff indicates append to the end of file.
|
|
*/
|
|
BOOLEAN FileOffsetAppend = (FileOffset->HighPart == (LONG)0xffffffff) &&
|
|
(FileOffset->LowPart == 0xffffffff);
|
|
|
|
BOOLEAN FileSizeModified = FALSE;
|
|
BOOLEAN ResourceAcquiredShared = FALSE;
|
|
|
|
/* Initialize some of the vars and pointers */
|
|
OldFileSize.QuadPart = 0;
|
|
OldValidDataLength.QuadPart = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
Offset.QuadPart = FileOffset->QuadPart + Length;
|
|
|
|
/* Nagar p.544.
|
|
* Check with Cc if we can write.
|
|
*/
|
|
if (!CcCanIWrite(FileObject, Length, TRUE, FALSE) ||
|
|
(FileObject->Flags & FO_WRITE_THROUGH))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
/* No actual read */
|
|
if (!Length)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
FsRtlEnterFileSystem();
|
|
|
|
/* Check we are going to extend the file */
|
|
if ((FileOffsetAppend == FALSE) &&
|
|
(FileOffset->QuadPart + Length <= FcbHeader->ValidDataLength.QuadPart))
|
|
{
|
|
/* Acquire the resource shared */
|
|
ExAcquireResourceSharedLite(FcbHeader->Resource, TRUE);
|
|
ResourceAcquiredShared = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Acquire the resource exclusive */
|
|
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
}
|
|
|
|
/* Check if we are appending */
|
|
if (FileOffsetAppend != FALSE)
|
|
{
|
|
Offset.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
NewSize.QuadPart = FcbHeader->FileSize.QuadPart + Length;
|
|
}
|
|
else
|
|
{
|
|
Offset.QuadPart = FileOffset->QuadPart;
|
|
NewSize.QuadPart = FileOffset->QuadPart + Length;
|
|
}
|
|
|
|
if ((FileObject->PrivateCacheMap) &&
|
|
(FcbHeader->IsFastIoPossible) &&
|
|
(Length <= MAXLONGLONG - FileOffset->QuadPart) &&
|
|
(NewSize.QuadPart <= FcbHeader->AllocationSize.QuadPart))
|
|
{
|
|
/* Check if we can keep the lock shared */
|
|
if (ResourceAcquiredShared &&
|
|
(NewSize.QuadPart > FcbHeader->ValidDataLength.QuadPart))
|
|
{
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
|
|
/* Compute the offset and the new filesize */
|
|
if (FileOffsetAppend)
|
|
{
|
|
Offset.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
NewSize.QuadPart = FcbHeader->FileSize.QuadPart + Length;
|
|
}
|
|
|
|
/* Recheck the above points since we released and reacquire the lock */
|
|
if ((FileObject->PrivateCacheMap != NULL) &&
|
|
(FcbHeader->IsFastIoPossible != FastIoIsNotPossible) &&
|
|
(FcbHeader->AllocationSize.QuadPart > NewSize.QuadPart))
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
/* Check if we need to find out if fast I/O is available */
|
|
if (FcbHeader->IsFastIoPossible == FastIoIsQuestionable)
|
|
{
|
|
/* Sanity check */
|
|
/* ASSERT(!KeIsExecutingDpc()); */
|
|
|
|
/* Get the Fast I/O table */
|
|
Device = IoGetRelatedDeviceObject(FileObject);
|
|
FastIoDispatch = Device->DriverObject->FastIoDispatch;
|
|
|
|
/* Sanity check */
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
/* Ask the driver if we can do it */
|
|
if (!FastIoDispatch->FastIoCheckIfPossible(FileObject,
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
FALSE,
|
|
IoStatus,
|
|
Device))
|
|
{
|
|
/* It's not, fail */
|
|
goto FailAndCleanup;
|
|
}
|
|
}
|
|
|
|
/* If we are going to modify the filesize,
|
|
* save the old fs in case the operation fails.
|
|
*/
|
|
if (NewSize.QuadPart > FcbHeader->FileSize.QuadPart)
|
|
{
|
|
FileSizeModified = TRUE;
|
|
OldFileSize.QuadPart = FcbHeader->FileSize.QuadPart;
|
|
OldValidDataLength.QuadPart = FcbHeader->ValidDataLength.QuadPart;
|
|
|
|
/* If the high part of the filesize is going
|
|
* to change, grab the Paging IoResouce.
|
|
*/
|
|
if (NewSize.HighPart != FcbHeader->FileSize.HighPart &&
|
|
FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->FileSize.QuadPart = NewSize.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->FileSize.QuadPart = NewSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
|
|
/* Nagar p.544.
|
|
* Set ourselves as top component.
|
|
*/
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if there is a gap between the end of the file and the offset.
|
|
* If yes, then we have to zero the data.
|
|
*/
|
|
if (Offset.QuadPart > FcbHeader->ValidDataLength.QuadPart)
|
|
{
|
|
Result = CcZeroData(FileObject,
|
|
&FcbHeader->ValidDataLength,
|
|
&Offset,
|
|
TRUE);
|
|
if (Result)
|
|
{
|
|
CcPrepareMdlWrite(FileObject,
|
|
&Offset,
|
|
Length,
|
|
MdlChain,
|
|
IoStatus);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CcPrepareMdlWrite(FileObject, &Offset, Length, MdlChain, IoStatus);
|
|
}
|
|
|
|
}
|
|
_SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reset the top component */
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
/* Did the operation suceeded */
|
|
if (Result)
|
|
{
|
|
/* Check if we need to update the filesize */
|
|
if (NewSize.QuadPart > FcbHeader->ValidDataLength.QuadPart)
|
|
{
|
|
if (NewSize.HighPart != FcbHeader->ValidDataLength.HighPart &&
|
|
FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->ValidDataLength.QuadPart = NewSize.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->ValidDataLength.QuadPart = NewSize.QuadPart;
|
|
}
|
|
}
|
|
|
|
/* Flag the file as modified */
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
/* Check if the filesize has changed */
|
|
if (FileSizeModified)
|
|
{
|
|
SharedCacheMap =
|
|
(PSHARED_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap;
|
|
SharedCacheMap->FileSize.QuadPart = NewSize.QuadPart;
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The operation did not succeed.
|
|
* Reset the file size to what it should be.
|
|
*/
|
|
if (FileSizeModified)
|
|
{
|
|
if (FcbHeader->PagingIoResource)
|
|
{
|
|
ExAcquireResourceExclusiveLite(FcbHeader->PagingIoResource, TRUE);
|
|
FcbHeader->FileSize.QuadPart = OldFileSize.QuadPart;
|
|
FcbHeader->ValidDataLength.QuadPart = OldValidDataLength.QuadPart;
|
|
ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
}
|
|
else
|
|
{
|
|
FcbHeader->FileSize.QuadPart = OldFileSize.QuadPart;
|
|
FcbHeader->ValidDataLength.QuadPart = OldValidDataLength.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
goto FailAndCleanup;
|
|
}
|
|
|
|
FailAndCleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
return FALSE;
|
|
|
|
Cleanup:
|
|
|
|
ExReleaseResourceLite(FcbHeader->Resource);
|
|
FsRtlExitFileSystem();
|
|
return Result;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlAcquireFileExclusiveCommon(IN PFILE_OBJECT FileObject,
|
|
IN FS_FILTER_SECTION_SYNC_TYPE SyncType,
|
|
IN ULONG Reserved)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
PEXTENDED_DRIVER_EXTENSION DriverExtension;
|
|
PFS_FILTER_CALLBACKS FilterCallbacks;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Get master FsRtl lock */
|
|
FsRtlEnterFileSystem();
|
|
|
|
DriverExtension = (PEXTENDED_DRIVER_EXTENSION)DeviceObject->DriverObject->DriverExtension;
|
|
FilterCallbacks = DriverExtension->FsFilterCallbacks;
|
|
|
|
/* Check if Filter Cllbacks are supported */
|
|
if (FilterCallbacks && FilterCallbacks->PreAcquireForSectionSynchronization)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID CompletionContext;
|
|
|
|
FS_FILTER_CALLBACK_DATA CbData;
|
|
|
|
RtlZeroMemory(&CbData, sizeof(CbData));
|
|
|
|
CbData.SizeOfFsFilterCallbackData = sizeof(CbData);
|
|
CbData.Operation = FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION;
|
|
CbData.DeviceObject = DeviceObject;
|
|
CbData.FileObject = FileObject;
|
|
CbData.Parameters.AcquireForSectionSynchronization.PageProtection = Reserved;
|
|
CbData.Parameters.AcquireForSectionSynchronization.SyncType = SyncType;
|
|
|
|
Status = FilterCallbacks->PreAcquireForSectionSynchronization(&CbData, &CompletionContext);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
FsRtlExitFileSystem();
|
|
return Status;
|
|
}
|
|
|
|
/* Should we do something in-between ? */
|
|
|
|
if (FilterCallbacks->PostAcquireForSectionSynchronization)
|
|
{
|
|
FilterCallbacks->PostAcquireForSectionSynchronization(&CbData, Status, CompletionContext);
|
|
}
|
|
|
|
/* Return here when the status is based on the synchonization type and write access to the file */
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ||
|
|
Status == STATUS_FILE_LOCKED_WITH_ONLY_READERS ||
|
|
Status == STATUS_FILE_LOCKED_WITH_WRITERS)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if Fast Calls are supported, and check AcquireFileForNtCreateSection */
|
|
if (FastDispatch &&
|
|
FastDispatch->AcquireFileForNtCreateSection)
|
|
{
|
|
/* Call the AcquireFileForNtCreateSection FastIo handler */
|
|
FastDispatch->AcquireFileForNtCreateSection(FileObject);
|
|
}
|
|
else
|
|
{
|
|
/* No FastIo handler, acquire file's resource exclusively */
|
|
if (FcbHeader && FcbHeader->Resource) ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlAcquireFileExclusive(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Call the common routine. Don't care about the result */
|
|
(VOID)FsRtlAcquireFileExclusiveCommon(FileObject, SyncTypeOther, 0);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlAcquireToCreateMappedSection(_In_ PFILE_OBJECT FileObject,
|
|
_In_ ULONG SectionPageProtection)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return FsRtlAcquireFileExclusiveCommon(FileObject, SyncTypeCreateSection, SectionPageProtection);
|
|
}
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlReleaseFile(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if Fast Calls are supported and check ReleaseFileForNtCreateSection */
|
|
if (FastDispatch &&
|
|
FastDispatch->ReleaseFileForNtCreateSection)
|
|
{
|
|
/* Call the ReleaseFileForNtCreateSection FastIo handler */
|
|
FastDispatch->ReleaseFileForNtCreateSection(FileObject);
|
|
}
|
|
else
|
|
{
|
|
/* No FastIo handler, release file's resource */
|
|
if (FcbHeader && FcbHeader->Resource) ExReleaseResourceLite(FcbHeader->Resource);
|
|
}
|
|
|
|
/* Release master FsRtl lock */
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlAcquireFileForCcFlushEx(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
NTSTATUS Status;
|
|
|
|
/* Get the Base File System (Volume) and Fast Calls */
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Get master FsRtl lock */
|
|
FsRtlEnterFileSystem();
|
|
|
|
/* Check if Fast Calls are supported, and check AcquireForCcFlush */
|
|
if (FastDispatch &&
|
|
FastDispatch->AcquireForCcFlush)
|
|
{
|
|
/* Call the AcquireForCcFlush FastIo handler */
|
|
Status = FastDispatch->AcquireForCcFlush(FileObject, BaseDeviceObject);
|
|
|
|
/* Return either success or inability to wait.
|
|
In case of other failure - fall through */
|
|
if (NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
if (Status == STATUS_CANT_WAIT)
|
|
{
|
|
DPRINT1("STATUS_CANT_WAIT\n");
|
|
FsRtlExitFileSystem();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* No FastIo handler (or it failed). Acquire Main resource */
|
|
if (FcbHeader->Resource)
|
|
{
|
|
/* Acquire it - either shared if it's already acquired
|
|
or exclusively if we are the first */
|
|
if (ExIsResourceAcquiredSharedLite(FcbHeader->Resource))
|
|
ExAcquireResourceSharedLite(FcbHeader->Resource, TRUE);
|
|
else
|
|
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
|
|
}
|
|
|
|
/* Also acquire its PagingIO resource */
|
|
if (FcbHeader->PagingIoResource)
|
|
ExAcquireResourceSharedLite(FcbHeader->PagingIoResource, TRUE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlAcquireFileForCcFlush(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Call the common routine. Don't care about the result */
|
|
(VOID)FsRtlAcquireFileForCcFlushEx(FileObject);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
FsRtlReleaseFileForCcFlush(IN PFILE_OBJECT FileObject)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if Fast Calls are supported, and check ReleaseForCcFlush */
|
|
if (FastDispatch &&
|
|
FastDispatch->ReleaseForCcFlush)
|
|
{
|
|
/* Call the ReleaseForCcFlush FastIo handler */
|
|
Status = FastDispatch->ReleaseForCcFlush(FileObject, BaseDeviceObject);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* No FastIo handler (or it failed). Release PagingIO resource and
|
|
then Main resource */
|
|
if (FcbHeader->PagingIoResource) ExReleaseResourceLite(FcbHeader->PagingIoResource);
|
|
if (FcbHeader->Resource) ExReleaseResourceLite(FcbHeader->Resource);
|
|
}
|
|
|
|
/* Release master FsRtl lock */
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
/**
|
|
* @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,
|
|
_Outptr_result_maybenull_ PERESOURCE *ResourceToRelease)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FcbHeader;
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
PERESOURCE ResourceToAcquire = NULL;
|
|
BOOLEAN Exclusive;
|
|
BOOLEAN Result;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* Get Device Object and Fast Calls */
|
|
FcbHeader = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
BaseDeviceObject = IoGetBaseFileSystemDeviceObject(FileObject);
|
|
FastDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check if Fast Calls are supported, and check AcquireForModWrite */
|
|
if (FastDispatch &&
|
|
FastDispatch->AcquireForModWrite)
|
|
{
|
|
/* Call the AcquireForModWrite FastIo handler */
|
|
Status = FastDispatch->AcquireForModWrite(FileObject,
|
|
EndingOffset,
|
|
ResourceToRelease,
|
|
BaseDeviceObject);
|
|
|
|
/* Return either success or inability to wait.
|
|
In case of other failure - fall through */
|
|
if (Status == STATUS_SUCCESS ||
|
|
Status == STATUS_CANT_WAIT)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Check what and how we should acquire */
|
|
Exclusive = FsRtlpGetResourceForModWrite(FcbHeader, EndingOffset, &ResourceToAcquire);
|
|
|
|
/* Acquire the resource and loop until we're sure we got this right. */
|
|
while (TRUE)
|
|
{
|
|
BOOLEAN OldExclusive;
|
|
PERESOURCE OldResourceToAcquire;
|
|
|
|
if (ResourceToAcquire == NULL)
|
|
{
|
|
/*
|
|
* There's nothing to acquire, we can simply return success
|
|
*/
|
|
|
|
break;
|
|
}
|
|
|
|
if (Exclusive)
|
|
{
|
|
Result = ExAcquireResourceExclusiveLite(ResourceToAcquire, FALSE);
|
|
}
|
|
else
|
|
{
|
|
Result = ExAcquireSharedWaitForExclusive(ResourceToAcquire, FALSE);
|
|
}
|
|
|
|
if (!Result)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* Can we fix this situation? */
|
|
if ((OldResourceToAcquire == ResourceToAcquire) && !Exclusive)
|
|
{
|
|
/* We can easily do so */
|
|
ExConvertExclusiveToSharedLite(ResourceToAcquire);
|
|
break;
|
|
}
|
|
|
|
/* Things have changed since we acquired the lock. Start again */
|
|
ExReleaseResourceLite(OldResourceToAcquire);
|
|
}
|
|
|
|
/* If we're here, this means that we succeeded */
|
|
*ResourceToRelease = ResourceToAcquire;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, BaseDeviceObject;
|
|
PFAST_IO_DISPATCH FastDispatch;
|
|
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 ReleaseForModWrite */
|
|
if (FastDispatch &&
|
|
FastDispatch->ReleaseForModWrite)
|
|
{
|
|
/* Call the ReleaseForModWrite FastIo handler */
|
|
Status = FastDispatch->ReleaseForModWrite(FileObject,
|
|
ResourceToRelease,
|
|
BaseDeviceObject);
|
|
}
|
|
|
|
/* Just release the resource if previous op failed */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ExReleaseResourceLite(ResourceToRelease);
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
* @name FsRtlRegisterFileSystemFilterCallbacks
|
|
* @unimplemented
|
|
*
|
|
* FILLME
|
|
*
|
|
* @param FilterDriverObject
|
|
* FILLME
|
|
*
|
|
* @param Callbacks
|
|
* FILLME
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
FsRtlRegisterFileSystemFilterCallbacks(
|
|
PDRIVER_OBJECT FilterDriverObject,
|
|
PFS_FILTER_CALLBACKS Callbacks)
|
|
{
|
|
PFS_FILTER_CALLBACKS NewCallbacks;
|
|
PEXTENDED_DRIVER_EXTENSION DriverExtension;
|
|
PAGED_CODE();
|
|
|
|
/* Verify parameters */
|
|
if ((FilterDriverObject == NULL) || (Callbacks == NULL))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Allocate a buffer for a copy of the callbacks */
|
|
NewCallbacks = ExAllocatePoolWithTag(NonPagedPool,
|
|
Callbacks->SizeOfFsFilterCallbacks,
|
|
'gmSF');
|
|
if (NewCallbacks == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Copy the callbacks */
|
|
RtlCopyMemory(NewCallbacks, Callbacks, Callbacks->SizeOfFsFilterCallbacks);
|
|
|
|
/* Set the callbacks in the driver extension */
|
|
DriverExtension = (PEXTENDED_DRIVER_EXTENSION)FilterDriverObject->DriverExtension;
|
|
DriverExtension->FsFilterCallbacks = NewCallbacks;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|