mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
2040 lines
64 KiB
C
2040 lines
64 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 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;
|
|
}
|