reactos/ntoskrnl/fsrtl/fastio.c
Timo Kreuzer 9ea495ba33 Create a branch for header work.
svn path=/branches/header-work/; revision=45691
2010-02-26 22:57:55 +00:00

1608 lines
51 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: buzdelabuz2@gmail.com,alex.ionescu@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(FcbHeader->FileSize.QuadPart >=
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 == TRUE)
{
ASSERT((IoStatus->Status == STATUS_END_OF_FILE) ||
((LONGLONG)(FileOffset->QuadPart + IoStatus->Information) <=
FcbHeader->FileSize.QuadPart));
}
}
/* Update the current file offset */
if (Result == TRUE)
{
FileObject->CurrentByteOffset.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 ResourceAquiredShared = 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);
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ == 405)
/* Silence incorrect GCC 4.5.x warning */
OldFileSize.LowPart = 0;
#endif
/* 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) == TRUE))
{
return FALSE;
}
/* No actual read */
if (!Length)
{
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = 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);
ResourceAquiredShared = 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 == TRUE)
{
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 (ResourceAquiredShared &&
(NewSize.LowPart > FcbHeader->ValidDataLength.LowPart + 0x2000))
{
/* Then we need to acquire the resource exclusive */
ExReleaseResourceLite(FcbHeader->Resource);
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
if (FileOffsetAppend == TRUE)
{
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 == TRUE)
{
/* 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;
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ == 405)
/* Silence incorrect GCC 4.5.x warning */
OldFileSize.QuadPart = 0;
#endif
/* 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;
}
ResourceAquiredShared = TRUE;
}
else
{
/* Acquire the resource exclusive */
if (!ExAcquireResourceExclusiveLite(FcbHeader->Resource, Wait))
{
goto LeaveCriticalAndFail;
}
}
/* Check if we are appending */
if (FileOffsetAppend == TRUE)
{
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 (ResourceAquiredShared &&
(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,
TRUE,
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 jump
* outside the SEH, so I a 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 =
ALIGN_UP(sizeof(FILE_INFORMATION_CLASS), ULONG);
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(MemoryDescriptorList, FileObject);
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 ResourceAquiredShared = 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);
ResourceAquiredShared = TRUE;
}
else
{
/* Acquire the resource exclusive */
ExAcquireResourceExclusiveLite(FcbHeader->Resource, TRUE);
}
/* Check if we are appending */
if (FileOffsetAppend == TRUE)
{
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 (ResourceAquiredShared &&
(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;
}
/*
* @implemented
*/
VOID
NTAPI
FsRtlAcquireFileExclusive(IN PFILE_OBJECT FileObject)
{
/* PAGED_CODE(); */
/* FsRtlAcquireFileExclusiveCommon(FileObject, 0, 0); */
KeBugCheck(FILE_SYSTEM);
}
/*
* @implemented
*/
VOID
NTAPI
FsRtlReleaseFile(IN PFILE_OBJECT FileObject)
{
KeBugCheck(FILE_SYSTEM);
}
/*++
* @name FsRtlRegisterFileSystemFilterCallbacks
* @unimplemented
*
* FILLME
*
* @param FilterDriverObject
* FILLME
*
* @param Callbacks
* FILLME
*
* @return None
*
* @remarks None
*
*--*/
NTSTATUS
NTAPI
FsRtlRegisterFileSystemFilterCallbacks(IN PDRIVER_OBJECT FilterDriverObject,
IN PFS_FILTER_CALLBACKS Callbacks)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}