You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4402 lines
131 KiB
4402 lines
131 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FastIo.c
|
|
|
|
Abstract:
|
|
|
|
The Fast I/O path is used to avoid calling the file systems directly to
|
|
do a cached read. This module is only used if the file object indicates
|
|
that caching is enabled (i.e., the private cache map is not null).
|
|
|
|
Author:
|
|
|
|
Gary Kimura [GaryKi] 25-Feb-1991
|
|
|
|
Revision History:
|
|
|
|
Tom Miller [TomM] 14-Apr-1991 Added Fast Write routines
|
|
|
|
--*/
|
|
|
|
#include "FsRtlP.h"
|
|
|
|
#if DBG
|
|
|
|
typedef struct _FS_RTL_DEBUG_COUNTERS {
|
|
|
|
ULONG AcquireFileExclusiveEx_Succeed;
|
|
ULONG AcquireFileExclusiveEx_Fail;
|
|
ULONG ReleaseFile;
|
|
|
|
ULONG AcquireFileForModWriteEx_Succeed;
|
|
ULONG AcquireFileForModWriteEx_Fail;
|
|
ULONG ReleaseFileForModWrite;
|
|
|
|
ULONG AcquireFileForCcFlushEx_Succeed;
|
|
ULONG AcquireFileForCcFlushEx_Fail;
|
|
ULONG ReleaseFileForCcFlush;
|
|
|
|
} FS_RTL_DEBUG_COUNTERS, *PFS_RTL_DEBUG_COUNTERS;
|
|
|
|
FS_RTL_DEBUG_COUNTERS gCounter = { 0, 0, 0,
|
|
0, 0, 0,
|
|
0, 0, 0 };
|
|
|
|
#endif
|
|
|
|
//
|
|
// Trace level for the module
|
|
//
|
|
|
|
#define Dbg (0x04000000)
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FsRtlCopyRead)
|
|
#pragma alloc_text(PAGE, FsRtlCopyWrite)
|
|
#pragma alloc_text(PAGE, FsRtlMdlRead)
|
|
#pragma alloc_text(PAGE, FsRtlMdlReadDev)
|
|
#pragma alloc_text(PAGE, FsRtlPrepareMdlWrite)
|
|
#pragma alloc_text(PAGE, FsRtlPrepareMdlWriteDev)
|
|
#pragma alloc_text(PAGE, FsRtlMdlWriteComplete)
|
|
#pragma alloc_text(PAGE, FsRtlMdlWriteCompleteDev)
|
|
#pragma alloc_text(PAGE, FsRtlAcquireFileForCcFlush)
|
|
#pragma alloc_text(PAGE, FsRtlAcquireFileForCcFlushEx)
|
|
#pragma alloc_text(PAGE, FsRtlReleaseFileForCcFlush)
|
|
#pragma alloc_text(PAGE, FsRtlAcquireFileExclusive)
|
|
#pragma alloc_text(PAGE, FsRtlAcquireToCreateMappedSection)
|
|
#pragma alloc_text(PAGE, FsRtlAcquireFileExclusiveCommon)
|
|
#pragma alloc_text(PAGE, FsRtlReleaseFile)
|
|
#pragma alloc_text(PAGE, FsRtlGetFileSize)
|
|
#pragma alloc_text(PAGE, FsRtlSetFileSize)
|
|
#pragma alloc_text(PAGE, FsRtlIncrementCcFastReadNotPossible )
|
|
#pragma alloc_text(PAGE, FsRtlIncrementCcFastReadWait )
|
|
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcCopyRead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
Wait - FALSE if caller may not block, TRUE otherwise
|
|
|
|
Buffer - Pointer to output buffer to which data should be copied.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if Wait was supplied as FALSE and the data was not delivered, or
|
|
if there is an I/O error.
|
|
|
|
TRUE - if the data is being delivered
|
|
|
|
--*/
|
|
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
BOOLEAN Status = TRUE;
|
|
ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES( FileOffset->QuadPart, Length );
|
|
LARGE_INTEGER BeyondLastByte;
|
|
PDEVICE_OBJECT targetVdo;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Special case a read of zero length
|
|
//
|
|
|
|
if (Length != 0) {
|
|
|
|
//
|
|
// Check for overflow. Returning false here will re-route this request through the
|
|
// IRP based path, but this isn't performance critical.
|
|
//
|
|
|
|
if (MAXLONGLONG - FileOffset->QuadPart < (LONGLONG)Length) {
|
|
|
|
IoStatus->Status = STATUS_INVALID_PARAMETER;
|
|
IoStatus->Information = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BeyondLastByte.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
Header = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
//
|
|
// Enter the file system
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//
|
|
// Increment performance counters and get the resource
|
|
//
|
|
|
|
if (Wait) {
|
|
|
|
HOT_STATISTIC(CcFastReadWait) += 1;
|
|
|
|
//
|
|
// Acquired shared on the common fcb header
|
|
//
|
|
|
|
(VOID)ExAcquireResourceSharedLite( Header->Resource, TRUE );
|
|
|
|
} else {
|
|
|
|
HOT_STATISTIC(CcFastReadNoWait) += 1;
|
|
|
|
//
|
|
// Acquired shared on the common fcb header, and return if we
|
|
// don't get it
|
|
//
|
|
|
|
if (!ExAcquireResourceSharedLite( Header->Resource, FALSE )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
CcFastReadResourceMiss += 1;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that the File is acquired shared, we can safely test if it
|
|
// is really cached and if we can do fast i/o and if not, then
|
|
// release the fcb and return.
|
|
//
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible)) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
HOT_STATISTIC(CcFastReadNotPossible) += 1;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if fast I/O is questionable and if so then go ask the
|
|
// file system the answer
|
|
//
|
|
|
|
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
|
|
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
targetVdo = IoGetRelatedDeviceObject( FileObject );
|
|
FastIoDispatch = targetVdo->DriverObject->FastIoDispatch;
|
|
|
|
|
|
//
|
|
// All file systems that set "Is Questionable" had better support
|
|
// fast I/O
|
|
//
|
|
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
//
|
|
// Call the file system to check for fast I/O. If the answer is
|
|
// anything other than GoForIt then we cannot take the fast I/O
|
|
// path.
|
|
//
|
|
|
|
if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
LockKey,
|
|
TRUE, // read operation
|
|
IoStatus,
|
|
targetVdo )) {
|
|
|
|
//
|
|
// Fast I/O is not possible so release the Fcb and return.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
HOT_STATISTIC(CcFastReadNotPossible) += 1;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for read past file size.
|
|
//
|
|
|
|
if ( BeyondLastByte.QuadPart > Header->FileSize.QuadPart ) {
|
|
|
|
if ( FileOffset->QuadPart >= Header->FileSize.QuadPart ) {
|
|
IoStatus->Status = STATUS_END_OF_FILE;
|
|
IoStatus->Information = 0;
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Length = (ULONG)( Header->FileSize.QuadPart - FileOffset->QuadPart );
|
|
}
|
|
|
|
//
|
|
// We can do fast i/o so call the cc routine to do the work and then
|
|
// release the fcb when we've done. If for whatever reason the
|
|
// copy read fails, then return FALSE to our caller.
|
|
//
|
|
// Also mark this as the top level "Irp" so that lower file system
|
|
// levels will not attempt a pop-up
|
|
//
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
try {
|
|
|
|
if (Wait && ((BeyondLastByte.HighPart | Header->FileSize.HighPart) == 0)) {
|
|
|
|
CcFastCopyRead( FileObject,
|
|
FileOffset->LowPart,
|
|
Length,
|
|
PageCount,
|
|
Buffer,
|
|
IoStatus );
|
|
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
|
|
ASSERT( (IoStatus->Status == STATUS_END_OF_FILE) ||
|
|
((FileOffset->LowPart + IoStatus->Information) <= Header->FileSize.LowPart));
|
|
|
|
} else {
|
|
|
|
Status = CcCopyRead( FileObject,
|
|
FileOffset,
|
|
Length,
|
|
Wait,
|
|
Buffer,
|
|
IoStatus );
|
|
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
|
|
ASSERT( !Status || (IoStatus->Status == STATUS_END_OF_FILE) ||
|
|
((LONGLONG)(FileOffset->QuadPart + IoStatus->Information) <= Header->FileSize.QuadPart));
|
|
}
|
|
|
|
if (Status) {
|
|
|
|
FileObject->CurrentByteOffset.QuadPart = FileOffset->QuadPart + IoStatus->Information;
|
|
}
|
|
|
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
Status = FALSE;
|
|
}
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
return Status;
|
|
|
|
} else {
|
|
|
|
//
|
|
// A zero length transfer was requested.
|
|
//
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FsRtlCopyWrite (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
IN PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached write bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy write
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcCopyWrite.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being write.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
Wait - FALSE if caller may not block, TRUE otherwise
|
|
|
|
Buffer - Pointer to output buffer to which data should be copied.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if Wait was supplied as FALSE and the data was not delivered, or
|
|
if there is an I/O error.
|
|
|
|
TRUE - if the data is being delivered
|
|
|
|
--*/
|
|
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
BOOLEAN AcquiredShared = FALSE;
|
|
BOOLEAN Status = TRUE;
|
|
BOOLEAN FileSizeChanged = FALSE;
|
|
BOOLEAN WriteToEndOfFile = (BOOLEAN)((FileOffset->LowPart == FILE_WRITE_TO_END_OF_FILE) &&
|
|
(FileOffset->HighPart == -1));
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a real pointer to the common fcb header
|
|
//
|
|
|
|
Header = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
//
|
|
// Do we need to verify the volume? If so, we must go to the file
|
|
// system. Also return FALSE if FileObject is write through, the
|
|
// File System must do that.
|
|
//
|
|
|
|
if (CcCanIWrite( FileObject, Length, Wait, FALSE ) &&
|
|
!FlagOn(FileObject->Flags, FO_WRITE_THROUGH) &&
|
|
CcCopyWriteWontFlush(FileObject, FileOffset, Length)) {
|
|
|
|
//
|
|
// Assume our transfer will work
|
|
//
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = Length;
|
|
|
|
//
|
|
// Special case the zero byte length
|
|
//
|
|
|
|
if (Length != 0) {
|
|
|
|
//
|
|
// Enter the file system
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//
|
|
// Split into separate paths for increased performance. First
|
|
// we have the faster path which only supports Wait == TRUE and
|
|
// 32 bits. We will make an unsafe test on whether the fast path
|
|
// is ok, then just return FALSE later if we were wrong. This
|
|
// should virtually never happen.
|
|
//
|
|
// IMPORTANT NOTE: It is very important that any changes made to
|
|
// this path also be applied to the 64-bit path
|
|
// which is the else of this test!
|
|
//
|
|
|
|
if (Wait && (Header->AllocationSize.HighPart == 0)) {
|
|
|
|
ULONG Offset, NewFileSize;
|
|
ULONG OldFileSize = 0;
|
|
ULONG OldValidDataLength = 0;
|
|
LOGICAL Wrapped;
|
|
|
|
//
|
|
// Make our best guess on whether we need the file exclusive
|
|
// or shared. Note that we do not check FileOffset->HighPart
|
|
// until below.
|
|
//
|
|
|
|
NewFileSize = FileOffset->LowPart + Length;
|
|
|
|
if (WriteToEndOfFile || (NewFileSize > Header->ValidDataLength.LowPart)) {
|
|
|
|
//
|
|
// Acquired shared on the common fcb header
|
|
//
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Acquired shared on the common fcb header
|
|
//
|
|
|
|
ExAcquireResourceSharedLite( Header->Resource, TRUE );
|
|
|
|
AcquiredShared = TRUE;
|
|
}
|
|
|
|
//
|
|
// We have the fcb shared now check if we can do fast i/o
|
|
// and if the file space is allocated, and if not then
|
|
// release the fcb and return.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize.LowPart;
|
|
NewFileSize = Header->FileSize.LowPart + Length;
|
|
Wrapped = NewFileSize < Header->FileSize.LowPart;
|
|
|
|
} else {
|
|
|
|
Offset = FileOffset->LowPart;
|
|
NewFileSize = FileOffset->LowPart + Length;
|
|
Wrapped = (NewFileSize < FileOffset->LowPart) || (FileOffset->HighPart != 0);
|
|
}
|
|
|
|
//
|
|
// Now that the File is acquired shared, we can safely test
|
|
// if it is really cached and if we can do fast i/o and we
|
|
// do not have to extend. If not then release the fcb and
|
|
// return.
|
|
//
|
|
// Get out if we have too much to zero. This case is not important
|
|
// for performance, and a file system supporting sparseness may have
|
|
// a way to do this more efficiently.
|
|
//
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
(NewFileSize > Header->AllocationSize.LowPart) ||
|
|
(Offset >= (Header->ValidDataLength.LowPart + 0x2000)) ||
|
|
(Header->AllocationSize.HighPart != 0) || Wrapped) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we will be extending ValidDataLength, we will have to
|
|
// get the Fcb exclusive, and make sure that FastIo is still
|
|
// possible. We should only execute this block of code very
|
|
// rarely, when the unsafe test for ValidDataLength failed
|
|
// above.
|
|
//
|
|
|
|
if (AcquiredShared && (NewFileSize > Header->ValidDataLength.LowPart)) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
|
|
//
|
|
// If writing to end of file, we must recalculate new size.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize.LowPart;
|
|
NewFileSize = Header->FileSize.LowPart + Length;
|
|
Wrapped = NewFileSize < Header->FileSize.LowPart;
|
|
}
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
(NewFileSize > Header->AllocationSize.LowPart) ||
|
|
(Header->AllocationSize.HighPart != 0) || Wrapped) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if fast I/O is questionable and if so then go ask
|
|
// the file system the answer
|
|
//
|
|
|
|
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
|
|
|
|
PDEVICE_OBJECT targetVdo = IoGetRelatedDeviceObject( FileObject );
|
|
PFAST_IO_DISPATCH FastIoDispatch = targetVdo->DriverObject->FastIoDispatch;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
//
|
|
// All file system then set "Is Questionable" had better
|
|
// support fast I/O
|
|
//
|
|
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
//
|
|
// Call the file system to check for fast I/O. If the
|
|
// answer is anything other than GoForIt then we cannot
|
|
// take the fast I/O path.
|
|
//
|
|
|
|
ASSERT(FILE_WRITE_TO_END_OF_FILE == 0xffffffff);
|
|
|
|
if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
|
|
FileOffset->QuadPart != (LONGLONG)-1 ?
|
|
FileOffset : &Header->FileSize,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
FALSE, // write operation
|
|
&IoStatus,
|
|
targetVdo )) {
|
|
|
|
//
|
|
// Fast I/O is not possible so release the Fcb and
|
|
// return.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now see if we will change FileSize. We have to do it now
|
|
// so that our reads are not nooped.
|
|
//
|
|
|
|
if (NewFileSize > Header->FileSize.LowPart) {
|
|
|
|
FileSizeChanged = TRUE;
|
|
OldFileSize = Header->FileSize.LowPart;
|
|
OldValidDataLength = Header->ValidDataLength.LowPart;
|
|
Header->FileSize.LowPart = NewFileSize;
|
|
}
|
|
|
|
//
|
|
// We can do fast i/o so call the cc routine to do the work
|
|
// and then release the fcb when we've done. If for whatever
|
|
// reason the copy write fails, then return FALSE to our
|
|
// caller.
|
|
//
|
|
// Also mark this as the top level "Irp" so that lower file
|
|
// system levels will not attempt a pop-up
|
|
//
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
try {
|
|
|
|
//
|
|
// See if we have to do some zeroing
|
|
//
|
|
|
|
if (Offset > Header->ValidDataLength.LowPart) {
|
|
|
|
LARGE_INTEGER ZeroEnd;
|
|
|
|
ZeroEnd.LowPart = Offset;
|
|
ZeroEnd.HighPart = 0;
|
|
|
|
CcZeroData( FileObject,
|
|
&Header->ValidDataLength,
|
|
&ZeroEnd,
|
|
TRUE );
|
|
}
|
|
|
|
CcFastCopyWrite( FileObject,
|
|
Offset,
|
|
Length,
|
|
Buffer );
|
|
|
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
Status = FALSE;
|
|
}
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
//
|
|
// If we succeeded, see if we have to update FileSize or
|
|
// ValidDataLength.
|
|
//
|
|
|
|
if (Status) {
|
|
|
|
//
|
|
// In the case of ValidDataLength, we really have to
|
|
// check again since we did not do this when we acquired
|
|
// the resource exclusive.
|
|
//
|
|
|
|
if (NewFileSize > Header->ValidDataLength.LowPart) {
|
|
|
|
Header->ValidDataLength.LowPart = NewFileSize;
|
|
}
|
|
|
|
//
|
|
// Set this handle as having modified the file
|
|
//
|
|
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
if (FileSizeChanged) {
|
|
|
|
CcGetFileSizePointer(FileObject)->LowPart = NewFileSize;
|
|
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
|
|
//
|
|
// Also update the file position pointer
|
|
//
|
|
|
|
FileObject->CurrentByteOffset.LowPart = Offset + Length;
|
|
FileObject->CurrentByteOffset.HighPart = 0;
|
|
|
|
//
|
|
// If we did not succeed, then we must restore the original
|
|
// FileSize while holding the PagingIoResource exclusive if
|
|
// it exists.
|
|
//
|
|
|
|
} else if (FileSizeChanged) {
|
|
|
|
if ( Header->PagingIoResource != NULL ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->FileSize.LowPart = OldFileSize;
|
|
Header->ValidDataLength.LowPart = OldValidDataLength;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->FileSize.LowPart = OldFileSize;
|
|
Header->ValidDataLength.LowPart = OldValidDataLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here is the 64-bit or no-wait path.
|
|
//
|
|
|
|
} else {
|
|
|
|
LARGE_INTEGER Offset, NewFileSize;
|
|
LARGE_INTEGER OldFileSize = {0};
|
|
LARGE_INTEGER OldValidDataLength = {0};
|
|
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
//
|
|
// Make our best guess on whether we need the file exclusive
|
|
// or shared.
|
|
//
|
|
|
|
NewFileSize.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
|
|
if (WriteToEndOfFile || (NewFileSize.QuadPart > Header->ValidDataLength.QuadPart)) {
|
|
|
|
//
|
|
// Acquired shared on the common fcb header, and return
|
|
// if we don't get it.
|
|
//
|
|
|
|
if (!ExAcquireResourceExclusiveLite( Header->Resource, Wait )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Acquired shared on the common fcb header, and return
|
|
// if we don't get it.
|
|
//
|
|
|
|
if (!ExAcquireResourceSharedLite( Header->Resource, Wait )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
AcquiredShared = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// We have the fcb shared now check if we can do fast i/o
|
|
// and if the file space is allocated, and if not then
|
|
// release the fcb and return.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize;
|
|
NewFileSize.QuadPart = Header->FileSize.QuadPart + (LONGLONG)Length;
|
|
|
|
} else {
|
|
|
|
Offset = *FileOffset;
|
|
NewFileSize.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
}
|
|
|
|
//
|
|
// Now that the File is acquired shared, we can safely test
|
|
// if it is really cached and if we can do fast i/o and we
|
|
// do not have to extend. If not then release the fcb and
|
|
// return.
|
|
//
|
|
// Get out if we are about to zero too much as well, as commented above.
|
|
// Likewise, for NewFileSizes that exceed MAXLONGLONG.
|
|
//
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
(Offset.QuadPart >= (Header->ValidDataLength.QuadPart + 0x2000)) ||
|
|
(MAXLONGLONG - Offset.QuadPart < (LONGLONG)Length) ||
|
|
(NewFileSize.QuadPart > Header->AllocationSize.QuadPart) ) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we will be extending ValidDataLength, we will have to
|
|
// get the Fcb exclusive, and make sure that FastIo is still
|
|
// possible. We should only execute this block of code very
|
|
// rarely, when the unsafe test for ValidDataLength failed
|
|
// above.
|
|
//
|
|
|
|
if (AcquiredShared && ( NewFileSize.QuadPart > Header->ValidDataLength.QuadPart )) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
|
|
if (!ExAcquireResourceExclusiveLite( Header->Resource, Wait )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If writing to end of file, we must recalculate new size.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize;
|
|
NewFileSize.QuadPart = Header->FileSize.QuadPart + (LONGLONG)Length;
|
|
}
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
( NewFileSize.QuadPart > Header->AllocationSize.QuadPart ) ) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if fast I/O is questionable and if so then go ask
|
|
// the file system the answer
|
|
//
|
|
|
|
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
|
|
|
|
PFAST_IO_DISPATCH FastIoDispatch = IoGetRelatedDeviceObject( FileObject )->DriverObject->FastIoDispatch;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
//
|
|
// All file system then set "Is Questionable" had better
|
|
// support fast I/O
|
|
//
|
|
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
//
|
|
// Call the file system to check for fast I/O. If the
|
|
// answer is anything other than GoForIt then we cannot
|
|
// take the fast I/O path.
|
|
//
|
|
|
|
ASSERT(FILE_WRITE_TO_END_OF_FILE == 0xffffffff);
|
|
|
|
if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
|
|
FileOffset->QuadPart != (LONGLONG)-1 ?
|
|
FileOffset : &Header->FileSize,
|
|
Length,
|
|
Wait,
|
|
LockKey,
|
|
FALSE, // write operation
|
|
&IoStatus,
|
|
DeviceObject )) {
|
|
|
|
//
|
|
// Fast I/O is not possible so release the Fcb and
|
|
// return.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now see if we will change FileSize. We have to do it now
|
|
// so that our reads are not nooped.
|
|
//
|
|
|
|
if ( NewFileSize.QuadPart > Header->FileSize.QuadPart ) {
|
|
|
|
FileSizeChanged = TRUE;
|
|
OldFileSize = Header->FileSize;
|
|
OldValidDataLength = Header->ValidDataLength;
|
|
|
|
//
|
|
// Deal with an extremely rare pathalogical case here the
|
|
// file size wraps.
|
|
//
|
|
|
|
if ( (Header->FileSize.HighPart != NewFileSize.HighPart) &&
|
|
(Header->PagingIoResource != NULL) ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->FileSize = NewFileSize;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->FileSize = NewFileSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We can do fast i/o so call the cc routine to do the work
|
|
// and then release the fcb when we've done. If for whatever
|
|
// reason the copy write fails, then return FALSE to our
|
|
// caller.
|
|
//
|
|
// Also mark this as the top level "Irp" so that lower file
|
|
// system levels will not attempt a pop-up
|
|
//
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
try {
|
|
|
|
//
|
|
// See if we have to do some zeroing
|
|
//
|
|
|
|
if ( Offset.QuadPart > Header->ValidDataLength.QuadPart ) {
|
|
|
|
Status = CcZeroData( FileObject,
|
|
&Header->ValidDataLength,
|
|
&Offset,
|
|
Wait );
|
|
}
|
|
|
|
if (Status) {
|
|
|
|
Status = CcCopyWrite( FileObject,
|
|
&Offset,
|
|
Length,
|
|
Wait,
|
|
Buffer );
|
|
}
|
|
|
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
Status = FALSE;
|
|
}
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
//
|
|
// If we succeeded, see if we have to update FileSize or
|
|
// ValidDataLength.
|
|
//
|
|
|
|
if (Status) {
|
|
|
|
//
|
|
// In the case of ValidDataLength, we really have to
|
|
// check again since we did not do this when we acquired
|
|
// the resource exclusive.
|
|
//
|
|
|
|
if ( NewFileSize.QuadPart > Header->ValidDataLength.QuadPart ) {
|
|
|
|
//
|
|
// Deal with an extremely rare pathalogical case here
|
|
// the ValidDataLength wraps.
|
|
//
|
|
|
|
if ( (Header->ValidDataLength.HighPart != NewFileSize.HighPart) &&
|
|
(Header->PagingIoResource != NULL) ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->ValidDataLength = NewFileSize;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->ValidDataLength = NewFileSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set this handle as having modified the file
|
|
//
|
|
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
if (FileSizeChanged) {
|
|
|
|
*CcGetFileSizePointer(FileObject) = NewFileSize;
|
|
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
|
|
//
|
|
// Also update the current file position pointer
|
|
//
|
|
|
|
FileObject->CurrentByteOffset.QuadPart = Offset.QuadPart + Length;
|
|
|
|
//
|
|
// If we did not succeed, then we must restore the original
|
|
// FileSize while holding the PagingIoResource exclusive if
|
|
// it exists.
|
|
//
|
|
|
|
} else if (FileSizeChanged) {
|
|
|
|
if ( Header->PagingIoResource != NULL ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->FileSize = OldFileSize;
|
|
Header->ValidDataLength = OldValidDataLength;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->FileSize = OldFileSize;
|
|
Header->ValidDataLength = OldValidDataLength;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
|
|
} else {
|
|
|
|
//
|
|
// A zero length transfer was requested.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The volume must be verified or the file is write through.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcMdlRead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
MdlChain - On output it returns a pointer to an MDL chain describing
|
|
the desired data.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
DeviceObject - Supplies DeviceObject for callee.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if the data was not delivered, or if there is an I/O error.
|
|
|
|
TRUE - if the data is being delivered
|
|
|
|
--*/
|
|
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
BOOLEAN Status = TRUE;
|
|
LARGE_INTEGER BeyondLastByte;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Special case a read of zero length
|
|
//
|
|
|
|
if (Length == 0) {
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
IoStatus->Information = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Overflows should've been handled by caller.
|
|
//
|
|
|
|
ASSERT(MAXLONGLONG - FileOffset->QuadPart >= (LONGLONG)Length);
|
|
|
|
|
|
//
|
|
// Get a real pointer to the common fcb header
|
|
//
|
|
|
|
BeyondLastByte.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
Header = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
//
|
|
// Enter the file system
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
CcFastMdlReadWait += 1;
|
|
|
|
//
|
|
// Acquired shared on the common fcb header
|
|
//
|
|
|
|
(VOID)ExAcquireResourceSharedLite( Header->Resource, TRUE );
|
|
|
|
//
|
|
// Now that the File is acquired shared, we can safely test if it is
|
|
// really cached and if we can do fast i/o and if not
|
|
// then release the fcb and return.
|
|
//
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible)) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
CcFastMdlReadNotPossible += 1;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if fast I/O is questionable and if so then go ask the file system
|
|
// the answer
|
|
//
|
|
|
|
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
|
|
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
ASSERT(!KeIsExecutingDpc());
|
|
|
|
FastIoDispatch = IoGetRelatedDeviceObject( FileObject )->DriverObject->FastIoDispatch;
|
|
|
|
|
|
//
|
|
// All file system then set "Is Questionable" had better support fast I/O
|
|
//
|
|
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
//
|
|
// Call the file system to check for fast I/O. If the answer is anything
|
|
// other than GoForIt then we cannot take the fast I/O path.
|
|
//
|
|
|
|
if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
TRUE, // read operation
|
|
IoStatus,
|
|
IoGetRelatedDeviceObject( FileObject ) )) {
|
|
|
|
//
|
|
// Fast I/O is not possible so release the Fcb and return.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
CcFastMdlReadNotPossible += 1;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for read past file size.
|
|
//
|
|
|
|
if ( BeyondLastByte.QuadPart > Header->FileSize.QuadPart ) {
|
|
|
|
if ( FileOffset->QuadPart >= Header->FileSize.QuadPart ) {
|
|
IoStatus->Status = STATUS_END_OF_FILE;
|
|
IoStatus->Information = 0;
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Length = (ULONG)( Header->FileSize.QuadPart - FileOffset->QuadPart );
|
|
}
|
|
|
|
//
|
|
// We can do fast i/o so call the cc routine to do the work and then
|
|
// release the fcb when we've done. If for whatever reason the
|
|
// mdl read fails, then return FALSE to our caller.
|
|
//
|
|
//
|
|
// Also mark this as the top level "Irp" so that lower file system levels
|
|
// will not attempt a pop-up
|
|
//
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
try {
|
|
|
|
CcMdlRead( FileObject, FileOffset, Length, MdlChain, IoStatus );
|
|
|
|
FileObject->Flags |= FO_FILE_FAST_IO_READ;
|
|
|
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
Status = FALSE;
|
|
}
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// The old routine will either dispatch or call FsRtlMdlReadDev
|
|
//
|
|
|
|
BOOLEAN
|
|
FsRtlMdlRead (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcMdlRead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
MdlChain - On output it returns a pointer to an MDL chain describing
|
|
the desired data.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if the data was not delivered, or if there is an I/O error.
|
|
|
|
TRUE - if the data is being delivered
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, VolumeDeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// See if the (top-level) FileSystem has a FastIo routine, and if so, call it.
|
|
//
|
|
|
|
if ((FastIoDispatch != NULL) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlRead)) &&
|
|
(FastIoDispatch->MdlRead != NULL)) {
|
|
|
|
return FastIoDispatch->MdlRead( FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the DeviceObject for the volume. If that DeviceObject is different, and
|
|
// it specifies the FastIo routine, then we have to return FALSE here and cause
|
|
// an Irp to get generated.
|
|
//
|
|
|
|
VolumeDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
if ((VolumeDeviceObject != DeviceObject) &&
|
|
(FastIoDispatch = VolumeDeviceObject->DriverObject->FastIoDispatch) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlRead)) &&
|
|
(FastIoDispatch->MdlRead != NULL)) {
|
|
|
|
return FALSE;
|
|
|
|
//
|
|
// Otherwise, call the default routine.
|
|
//
|
|
|
|
} else {
|
|
|
|
return FsRtlMdlReadDev( FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// The old routine will either dispatch or call FsRtlMdlReadCompleteDev
|
|
//
|
|
|
|
BOOLEAN
|
|
FsRtlMdlReadComplete (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PMDL MdlChain
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
MdlChain - Supplies a pointer to an MDL chain returned from CcMdlRead.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, VolumeDeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// See if the (top-level) FileSystem has a FastIo routine, and if so, call it.
|
|
//
|
|
|
|
if ((FastIoDispatch != NULL) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlReadComplete)) &&
|
|
(FastIoDispatch->MdlReadComplete != NULL)) {
|
|
|
|
return FastIoDispatch->MdlReadComplete( FileObject, MdlChain, DeviceObject );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the DeviceObject for the volume. If that DeviceObject is different, and
|
|
// it specifies the FastIo routine, then we have to return FALSE here and cause
|
|
// an Irp to get generated.
|
|
//
|
|
|
|
VolumeDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
if ((VolumeDeviceObject != DeviceObject) &&
|
|
(FastIoDispatch = VolumeDeviceObject->DriverObject->FastIoDispatch) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlReadComplete)) &&
|
|
(FastIoDispatch->MdlReadComplete != NULL)) {
|
|
|
|
return FALSE;
|
|
|
|
//
|
|
// Otherwise, call the default routine.
|
|
//
|
|
|
|
} else {
|
|
|
|
return FsRtlMdlReadCompleteDev( FileObject, MdlChain, DeviceObject );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FsRtlMdlReadCompleteDev (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PMDL MdlChain,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
MdlChain - Supplies a pointer to an MDL chain returned from CcMdlRead.
|
|
|
|
DeviceObject - Supplies the DeviceObject for the callee.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
CcMdlReadComplete2( FileObject, MdlChain );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
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
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcMdlRead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
MdlChain - On output it returns a pointer to an MDL chain describing
|
|
the desired data.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
DeviceObject - Supplies the DeviceObject for the callee.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if the data was not written, or if there is an I/O error.
|
|
|
|
TRUE - if the data is being written
|
|
|
|
--*/
|
|
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
LARGE_INTEGER Offset, NewFileSize;
|
|
LARGE_INTEGER OldFileSize = {0};
|
|
LARGE_INTEGER OldValidDataLength = {0};
|
|
BOOLEAN Status = TRUE;
|
|
BOOLEAN AcquiredShared = FALSE;
|
|
BOOLEAN FileSizeChanged = FALSE;
|
|
BOOLEAN WriteToEndOfFile = (BOOLEAN)((FileOffset->LowPart == FILE_WRITE_TO_END_OF_FILE) &&
|
|
(FileOffset->HighPart == -1));
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Call CcCanIWrite. Also return FALSE if FileObject is write through,
|
|
// the File System must do that.
|
|
//
|
|
|
|
if ( !CcCanIWrite( FileObject, Length, TRUE, FALSE ) ||
|
|
FlagOn( FileObject->Flags, FO_WRITE_THROUGH )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Assume our transfer will work
|
|
//
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Special case the zero byte length
|
|
//
|
|
|
|
if (Length == 0) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Get a real pointer to the common fcb header
|
|
//
|
|
|
|
Header = (PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext;
|
|
|
|
//
|
|
// Enter the file system
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//
|
|
// Make our best guess on whether we need the file exclusive or
|
|
// shared.
|
|
//
|
|
|
|
NewFileSize.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
|
|
if (WriteToEndOfFile || (NewFileSize.QuadPart > Header->ValidDataLength.QuadPart)) {
|
|
|
|
//
|
|
// Acquired exclusive on the common fcb header, and return if we don't
|
|
// get it.
|
|
//
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Acquired shared on the common fcb header, and return if we don't
|
|
// get it.
|
|
//
|
|
|
|
ExAcquireResourceSharedLite( Header->Resource, TRUE );
|
|
|
|
AcquiredShared = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// We have the fcb shared now check if we can do fast i/o and if the file
|
|
// space is allocated, and if not then release the fcb and return.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize;
|
|
NewFileSize.QuadPart = Header->FileSize.QuadPart + (LONGLONG)Length;
|
|
|
|
} else {
|
|
|
|
Offset = *FileOffset;
|
|
NewFileSize.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
|
|
}
|
|
|
|
//
|
|
// Now that the File is acquired shared, we can safely test if it is
|
|
// really cached and if we can do fast i/o and we do not have to extend.
|
|
// If not then release the fcb and return.
|
|
//
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
(MAXLONGLONG - Offset.QuadPart < (LONGLONG)Length) ||
|
|
( NewFileSize.QuadPart > Header->AllocationSize.QuadPart ) ) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we will be extending ValidDataLength, we will have to get the
|
|
// Fcb exclusive, and make sure that FastIo is still possible.
|
|
//
|
|
|
|
if (AcquiredShared && ( NewFileSize.QuadPart > Header->ValidDataLength.QuadPart )) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
|
|
AcquiredShared = FALSE;
|
|
|
|
//
|
|
// If writing to end of file, we must recalculate new size.
|
|
//
|
|
|
|
if (WriteToEndOfFile) {
|
|
|
|
Offset = Header->FileSize;
|
|
NewFileSize.QuadPart = Header->FileSize.QuadPart + (LONGLONG)Length;
|
|
}
|
|
|
|
if ((FileObject->PrivateCacheMap == NULL) ||
|
|
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
|
|
( NewFileSize.QuadPart > Header->AllocationSize.QuadPart )) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if fast I/O is questionable and if so then go ask the file system
|
|
// the answer
|
|
//
|
|
|
|
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
|
|
|
|
PFAST_IO_DISPATCH FastIoDispatch = IoGetRelatedDeviceObject( FileObject )->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// All file system then set "Is Questionable" had better support fast I/O
|
|
//
|
|
|
|
ASSERT(FastIoDispatch != NULL);
|
|
ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
|
|
|
|
//
|
|
// Call the file system to check for fast I/O. If the answer is anything
|
|
// other than GoForIt then we cannot take the fast I/O path.
|
|
//
|
|
|
|
if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
|
|
FileOffset,
|
|
Length,
|
|
TRUE,
|
|
LockKey,
|
|
FALSE, // write operation
|
|
IoStatus,
|
|
IoGetRelatedDeviceObject( FileObject ) )) {
|
|
|
|
//
|
|
// Fast I/O is not possible so release the Fcb and return.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
FsRtlExitFileSystem();
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now see if we will change FileSize. We have to do it now so that our
|
|
// reads are not nooped.
|
|
//
|
|
|
|
if ( NewFileSize.QuadPart > Header->FileSize.QuadPart ) {
|
|
|
|
FileSizeChanged = TRUE;
|
|
OldFileSize = Header->FileSize;
|
|
OldValidDataLength = Header->ValidDataLength;
|
|
|
|
//
|
|
// Deal with an extremely rare pathalogical case here the file
|
|
// size wraps.
|
|
//
|
|
|
|
if ( (Header->FileSize.HighPart != NewFileSize.HighPart) &&
|
|
(Header->PagingIoResource != NULL) ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->FileSize = NewFileSize;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->FileSize = NewFileSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We can do fast i/o so call the cc routine to do the work and then
|
|
// release the fcb when we've done. If for whatever reason the
|
|
// copy write fails, then return FALSE to our caller.
|
|
//
|
|
//
|
|
// Also mark this as the top level "Irp" so that lower file system levels
|
|
// will not attempt a pop-up
|
|
//
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = FSRTL_FAST_IO_TOP_LEVEL_IRP;
|
|
|
|
try {
|
|
|
|
//
|
|
// See if we have to do some zeroing
|
|
//
|
|
|
|
if ( Offset.QuadPart > Header->ValidDataLength.QuadPart ) {
|
|
|
|
Status = CcZeroData( FileObject,
|
|
&Header->ValidDataLength,
|
|
&Offset,
|
|
TRUE );
|
|
}
|
|
|
|
if (Status) {
|
|
|
|
CcPrepareMdlWrite( FileObject, &Offset, Length, MdlChain, IoStatus );
|
|
}
|
|
|
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
|
|
Status = FALSE;
|
|
}
|
|
|
|
PsGetCurrentThread()->TopLevelIrp = 0;
|
|
|
|
//
|
|
// If we succeeded, see if we have to update FileSize or ValidDataLength.
|
|
//
|
|
|
|
if (Status) {
|
|
|
|
//
|
|
// In the case of ValidDataLength, we really have to check again
|
|
// since we did not do this when we acquired the resource exclusive.
|
|
//
|
|
|
|
if ( NewFileSize.QuadPart > Header->ValidDataLength.QuadPart ) {
|
|
|
|
//
|
|
// Deal with an extremely rare pathalogical case here the
|
|
// ValidDataLength wraps.
|
|
//
|
|
|
|
if ( (Header->ValidDataLength.HighPart != NewFileSize.HighPart) &&
|
|
(Header->PagingIoResource != NULL) ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->ValidDataLength = NewFileSize;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->ValidDataLength = NewFileSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set this handle as having modified the file
|
|
//
|
|
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
|
|
if (FileSizeChanged) {
|
|
|
|
*CcGetFileSizePointer(FileObject) = NewFileSize;
|
|
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
}
|
|
|
|
//
|
|
// If we did not succeed, then we must restore the original FileSize
|
|
// and release the resource. In the success path, the cache manager
|
|
// will release the resource.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (FileSizeChanged) {
|
|
|
|
if ( Header->PagingIoResource != NULL ) {
|
|
|
|
(VOID)ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
|
|
Header->FileSize = OldFileSize;
|
|
Header->ValidDataLength = OldValidDataLength;
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
|
|
} else {
|
|
|
|
Header->FileSize = OldFileSize;
|
|
Header->ValidDataLength = OldValidDataLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we can release the resource.
|
|
//
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// The old routine will either dispatch or call FsRtlPrepareMdlWriteDev
|
|
//
|
|
|
|
BOOLEAN
|
|
FsRtlPrepareMdlWrite (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG LockKey,
|
|
OUT PMDL *MdlChain,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a fast cached mdl read bypassing the usual file system
|
|
entry routine (i.e., without the Irp). It is used to do a copy read
|
|
of a cached file object. For a complete description of the arguments
|
|
see CcMdlRead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
FileOffset - Byte offset in file for desired data.
|
|
|
|
Length - Length of desired data in bytes.
|
|
|
|
MdlChain - On output it returns a pointer to an MDL chain describing
|
|
the desired data.
|
|
|
|
IoStatus - Pointer to standard I/O status block to receive the status
|
|
for the transfer.
|
|
|
|
Return Value:
|
|
|
|
FALSE - if the data was not written, or if there is an I/O error.
|
|
|
|
TRUE - if the data is being written
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, VolumeDeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// See if the (top-level) FileSystem has a FastIo routine, and if so, call it.
|
|
//
|
|
|
|
if ((FastIoDispatch != NULL) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, PrepareMdlWrite)) &&
|
|
(FastIoDispatch->PrepareMdlWrite != NULL)) {
|
|
|
|
return FastIoDispatch->PrepareMdlWrite( FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the DeviceObject for the volume. If that DeviceObject is different, and
|
|
// it specifies the FastIo routine, then we have to return FALSE here and cause
|
|
// an Irp to get generated.
|
|
//
|
|
|
|
VolumeDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
if ((VolumeDeviceObject != DeviceObject) &&
|
|
(FastIoDispatch = VolumeDeviceObject->DriverObject->FastIoDispatch) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, PrepareMdlWrite)) &&
|
|
(FastIoDispatch->PrepareMdlWrite != NULL)) {
|
|
|
|
return FALSE;
|
|
|
|
//
|
|
// Otherwise, call the default routine.
|
|
//
|
|
|
|
} else {
|
|
|
|
return FsRtlPrepareMdlWriteDev( FileObject, FileOffset, Length, LockKey, MdlChain, IoStatus, DeviceObject );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// The old routine will either dispatch or call FsRtlMdlWriteCompleteDev
|
|
//
|
|
|
|
BOOLEAN
|
|
FsRtlMdlWriteComplete (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN PMDL MdlChain
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes an Mdl write.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
MdlChain - Supplies a pointer to an MDL chain returned from CcMdlPrepareMdlWrite.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT DeviceObject, VolumeDeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
//
|
|
// See if the (top-level) FileSystem has a FastIo routine, and if so, call it.
|
|
//
|
|
|
|
if ((FastIoDispatch != NULL) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteComplete)) &&
|
|
(FastIoDispatch->MdlWriteComplete != NULL)) {
|
|
|
|
return FastIoDispatch->MdlWriteComplete( FileObject, FileOffset, MdlChain, DeviceObject );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the DeviceObject for the volume. If that DeviceObject is different, and
|
|
// it specifies the FastIo routine, then we have to return FALSE here and cause
|
|
// an Irp to get generated.
|
|
//
|
|
|
|
VolumeDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
if ((VolumeDeviceObject != DeviceObject) &&
|
|
(FastIoDispatch = VolumeDeviceObject->DriverObject->FastIoDispatch) &&
|
|
(FastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteComplete)) &&
|
|
(FastIoDispatch->MdlWriteComplete != NULL)) {
|
|
|
|
return FALSE;
|
|
|
|
//
|
|
// Otherwise, call the default routine.
|
|
//
|
|
|
|
} else {
|
|
|
|
return FsRtlMdlWriteCompleteDev( FileObject, FileOffset, MdlChain, DeviceObject );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FsRtlMdlWriteCompleteDev (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN PMDL MdlChain,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes an Mdl write.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being read.
|
|
|
|
MdlChain - Supplies a pointer to an MDL chain returned from CcMdlPrepareMdlWrite.
|
|
|
|
DeviceObject - Supplies the DeviceObject for the callee.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Do not support WRITE_THROUGH in the fast path call.
|
|
//
|
|
|
|
if (FlagOn( FileObject->Flags, FO_WRITE_THROUGH )) {
|
|
return FALSE;
|
|
}
|
|
|
|
CcMdlWriteComplete2( FileObject, FileOffset, MdlChain );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
FsRtlRegisterFileSystemFilterCallbacks (
|
|
IN PDRIVER_OBJECT FilterDriverObject,
|
|
IN PFS_FILTER_CALLBACKS Callbacks
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers the FilterDriverObject to receive the
|
|
notifications specified in Callbacks at the appropriate times
|
|
for the devices to which this driver is attached.
|
|
|
|
This should only be called by a file system filter during
|
|
its DriverEntry routine.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
EndingOffset - The offset of the last byte being written + 1.
|
|
|
|
ByteCount - Length of data in bytes.
|
|
|
|
ResourceToRelease - Returns the resource to release. Not defined if
|
|
FALSE is returned.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - The callbacks were successfully registered
|
|
for this driver.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - There wasn't enough memory to
|
|
store these callbacks for the driver.
|
|
|
|
STATUS_INVALID_PARAMETER - Returned in any of the parameters
|
|
are invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_EXTENSION DriverExt;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!(ARGUMENT_PRESENT( FilterDriverObject ) &&
|
|
ARGUMENT_PRESENT( Callbacks ))) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
DriverExt = FilterDriverObject->DriverExtension;
|
|
|
|
FsFilterCallbacks = ExAllocatePoolWithTag( NonPagedPool,
|
|
Callbacks->SizeOfFsFilterCallbacks,
|
|
FSRTL_FILTER_MEMORY_TAG );
|
|
|
|
if (FsFilterCallbacks == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory( FsFilterCallbacks,
|
|
Callbacks,
|
|
Callbacks->SizeOfFsFilterCallbacks );
|
|
|
|
DriverExt->FsFilterCallbacks = FsFilterCallbacks;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
FsRtlAcquireFileForModWrite (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER EndingOffset,
|
|
OUT PERESOURCE *ResourceToRelease
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decides which file system resource the modified page
|
|
writer should acquire and acquires it if possible. Wait is always
|
|
specified as FALSE. We pass back the resource Mm has to release
|
|
when the write completes.
|
|
|
|
This routine is obsolete --- should call FsRtlAcquireFileForModWriteEx
|
|
instead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
EndingOffset - The offset of the last byte being written + 1.
|
|
|
|
ByteCount - Length of data in bytes.
|
|
|
|
ResourceToRelease - Returns the resource to release. Not defined if
|
|
FALSE is returned.
|
|
|
|
Return Value:
|
|
|
|
FALSE - The resource could not be acquired without waiting.
|
|
|
|
TRUE - The returned resource has been acquired.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Just call the new version of this routine and process
|
|
// the NTSTATUS returned into TRUE for success and FALSE
|
|
// for failure.
|
|
//
|
|
|
|
Status = FsRtlAcquireFileForModWriteEx( FileObject,
|
|
EndingOffset,
|
|
ResourceToRelease );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
FsRtlAcquireFileForModWriteEx (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER EndingOffset,
|
|
OUT PERESOURCE *ResourceToRelease
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decides which file system resource the modified page
|
|
writer should acquire and acquires it if possible. Wait is always
|
|
specified as FALSE. We pass back the resource Mm has to release
|
|
when the write completes.
|
|
|
|
The operation is presented to any file system filters attached to this
|
|
volume before and after the file system is asked to acquire this resource.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
EndingOffset - The offset of the last byte being written + 1.
|
|
|
|
ByteCount - Length of data in bytes.
|
|
|
|
ResourceToRelease - Returns the resource to release. Not defined if
|
|
FALSE is returned.
|
|
|
|
Return Value:
|
|
|
|
FALSE - The resource could not be acquired without waiting.
|
|
|
|
TRUE - The returned resource has been acquired.
|
|
|
|
--*/
|
|
{
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PDEVICE_OBJECT BaseFsDeviceObject;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CALLBACK_DATA CallbackData;
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
PERESOURCE ResourceAcquired;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
BOOLEAN AcquireExclusive;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseFsDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireForModWrite ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForModifiedPageWriter ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForModifiedPageWriter )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
//
|
|
// Call routine to initialize the control structure.
|
|
//
|
|
|
|
Status = FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_ACQUIRE_FOR_MOD_WRITE,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
TRUE );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize the operation-specific parameters in the callback data.
|
|
//
|
|
|
|
CallbackData = &(FsFilterCtrl.Data);
|
|
CallbackData->Parameters.AcquireForModifiedPageWriter.EndingOffset = EndingOffset;
|
|
CallbackData->Parameters.AcquireForModifiedPageWriter.ResourceToRelease = ResourceToRelease;
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
TRUE,
|
|
TRUE,
|
|
&BaseFsFailedOperation );
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseFsDeviceReference = TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForModifiedPageWriter ))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireForModWrite )) {
|
|
|
|
Status = FastIoDispatch->AcquireForModWrite( FileObject,
|
|
EndingOffset,
|
|
ResourceToRelease,
|
|
BaseFsDeviceObject );
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseFsDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_CANT_WAIT) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST) );
|
|
|
|
//
|
|
// If the base file system didn't have an AcquireForModWrite handler
|
|
// or couldn't return STATUS_SUCCESS or STATUS_CANT_WAIT,
|
|
// we need to perform the default actions here.
|
|
//
|
|
|
|
if ((Status != STATUS_SUCCESS) &&
|
|
(Status != STATUS_CANT_WAIT) &&
|
|
BaseFsFailedOperation) {
|
|
|
|
//
|
|
// We follow the following rules to determine which resource
|
|
// to acquire. We use the flags in the common header. These
|
|
// flags can't change once we have acquired any resource.
|
|
// This means we can do an unsafe test and optimisticly
|
|
// acquire a resource. At that point we can test the bits
|
|
// to see if we have what we want.
|
|
//
|
|
// 0 - If there is no main resource, acquire nothing.
|
|
//
|
|
// 1 - Acquire the main resource exclusively if the
|
|
// ACQUIRE_MAIN_RSRC_EX flag is set or we are extending
|
|
// valid data.
|
|
//
|
|
// 2 - Acquire the main resource shared if there is
|
|
// no paging io resource or the
|
|
// ACQUIRE_MAIN_RSRC_SH flag is set.
|
|
//
|
|
// 3 - Otherwise acquire the paging io resource shared.
|
|
//
|
|
|
|
Header = (PFSRTL_COMMON_FCB_HEADER) FileObject->FsContext;
|
|
|
|
if (Header->Resource == NULL) {
|
|
|
|
*ResourceToRelease = NULL;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto FsRtlAcquireFileForModWrite_CallCompletionCallbacks;
|
|
}
|
|
|
|
if (FlagOn( Header->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX ) ||
|
|
(EndingOffset->QuadPart > Header->ValidDataLength.QuadPart &&
|
|
Header->ValidDataLength.QuadPart != Header->FileSize.QuadPart)) {
|
|
|
|
ResourceAcquired = Header->Resource;
|
|
AcquireExclusive = TRUE;
|
|
|
|
} else if (FlagOn( Header->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH ) ||
|
|
Header->PagingIoResource == NULL) {
|
|
|
|
ResourceAcquired = Header->Resource;
|
|
AcquireExclusive = FALSE;
|
|
|
|
} else {
|
|
|
|
ResourceAcquired = Header->PagingIoResource;
|
|
AcquireExclusive = FALSE;
|
|
}
|
|
|
|
//
|
|
// Perform the following in a loop in case we need to back and
|
|
// check the state of the resource acquisition. In most cases
|
|
// the initial checks will succeed and we can proceed immediately.
|
|
// We have to worry about the two FsRtl bits changing but
|
|
// if there is no paging io resource before there won't ever be
|
|
// one.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Now acquire the desired resource.
|
|
//
|
|
|
|
if (AcquireExclusive) {
|
|
|
|
if (!ExAcquireResourceExclusiveLite( ResourceAcquired, FALSE )) {
|
|
|
|
Status = STATUS_CANT_WAIT;
|
|
goto FsRtlAcquireFileForModWrite_CallCompletionCallbacks;
|
|
}
|
|
|
|
} else if (!ExAcquireSharedWaitForExclusive( ResourceAcquired, FALSE )) {
|
|
|
|
Status = STATUS_CANT_WAIT;
|
|
goto FsRtlAcquireFileForModWrite_CallCompletionCallbacks;
|
|
}
|
|
|
|
//
|
|
// If the valid data length is changing or the exclusive bit is
|
|
// set and we don't have the main resource exclusive then
|
|
// release the current resource and acquire the main resource
|
|
// exclusively and move to the top of the loop.
|
|
//
|
|
// We must get it exclusive in all cases where the ending offset
|
|
// is beyond vdl. It used to be allowed shared if vdl == fs, but
|
|
// this neglected the possibility that the file could be extended
|
|
// under our shared (pagingio) access.
|
|
//
|
|
|
|
if (FlagOn( Header->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_EX ) ||
|
|
EndingOffset->QuadPart > Header->ValidDataLength.QuadPart) {
|
|
|
|
//
|
|
// If we don't have the main resource exclusively then
|
|
// release the current resource and attempt to acquire
|
|
// the main resource exclusively.
|
|
//
|
|
|
|
if (!AcquireExclusive) {
|
|
|
|
ExReleaseResourceLite( ResourceAcquired );
|
|
AcquireExclusive = TRUE;
|
|
ResourceAcquired = Header->Resource;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We have the correct resource. Exit the loop.
|
|
//
|
|
|
|
//
|
|
// If we should be acquiring the main resource shared then move
|
|
// to acquire the correct resource and proceed to the top of the loop.
|
|
//
|
|
|
|
} else if (FlagOn( Header->Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH )) {
|
|
|
|
//
|
|
// If we have the main resource exclusively then downgrade to
|
|
// shared and exit the loop.
|
|
//
|
|
|
|
if (AcquireExclusive) {
|
|
|
|
ExConvertExclusiveToSharedLite( ResourceAcquired );
|
|
|
|
//
|
|
// If we have the paging io resource then give up this resource
|
|
// and acquire the main resource exclusively. This is going
|
|
// at it with a large hammer but is guaranteed to be resolved
|
|
// in the next pass through the loop.
|
|
//
|
|
|
|
} else if (ResourceAcquired != Header->Resource) {
|
|
|
|
ExReleaseResourceLite( ResourceAcquired );
|
|
ResourceAcquired = Header->Resource;
|
|
AcquireExclusive = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We have the correct resource. Exit the loop.
|
|
//
|
|
|
|
//
|
|
// At this point we should have the paging Io resource shared
|
|
// if it exists. If not then acquire it shared and release the
|
|
// other resource and exit the loop.
|
|
//
|
|
|
|
} else if (Header->PagingIoResource != NULL
|
|
&& ResourceAcquired != Header->PagingIoResource) {
|
|
|
|
ResourceAcquired = NULL;
|
|
|
|
if (ExAcquireSharedWaitForExclusive( Header->PagingIoResource, FALSE )) {
|
|
|
|
ResourceAcquired = Header->PagingIoResource;
|
|
}
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
|
|
if (ResourceAcquired == NULL) {
|
|
|
|
Status = STATUS_CANT_WAIT;
|
|
goto FsRtlAcquireFileForModWrite_CallCompletionCallbacks;
|
|
}
|
|
|
|
//
|
|
// We now have the correct resource. Exit the loop.
|
|
//
|
|
|
|
//
|
|
// We should have the main resource shared. If we don't then
|
|
// degrade our lock to shared access.
|
|
//
|
|
|
|
} else if (AcquireExclusive) {
|
|
|
|
ExConvertExclusiveToSharedLite( ResourceAcquired );
|
|
|
|
//
|
|
// We now have the correct resource. Exit the loop.
|
|
//
|
|
}
|
|
|
|
//
|
|
// We have the correct resource. Exit the loop.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
*ResourceToRelease = ResourceAcquired;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
FsRtlAcquireFileForModWrite_CallCompletionCallbacks:
|
|
|
|
//
|
|
// Again, we only want to call try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
gCounter.AcquireFileForModWriteEx_Succeed ++;
|
|
|
|
} else {
|
|
|
|
gCounter.AcquireFileForModWriteEx_Fail ++;
|
|
}
|
|
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
FsRtlReleaseFileForModWrite (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PERESOURCE ResourceToRelease
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine releases a file system resource previously acquired for
|
|
the modified page writer.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
ResourceToRelease - Supplies the resource to release. Not defined if
|
|
FALSE is returned.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT BaseFsDeviceObject, DeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CALLBACK_DATA CallbackData;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
#if DBG
|
|
gCounter.ReleaseFileForModWrite ++;
|
|
#endif
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, ReleaseForModWrite ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForModifiedPageWriter ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForModifiedPageWriter )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_RELEASE_FOR_MOD_WRITE,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
FALSE );
|
|
|
|
//
|
|
// Initialize the operation-specific parameters in the callback data.
|
|
//
|
|
|
|
CallbackData = &(FsFilterCtrl.Data);
|
|
CallbackData->Parameters.ReleaseForModifiedPageWriter.ResourceToRelease = ResourceToRelease;
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
FALSE,
|
|
TRUE,
|
|
&BaseFsFailedOperation );
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseDeviceReference = TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForModifiedPageWriter ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForModifiedPageWriter ))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, ReleaseForModWrite )) {
|
|
|
|
Status = FastIoDispatch->ReleaseForModWrite( FileObject,
|
|
ResourceToRelease,
|
|
BaseFsDeviceObject );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST) );
|
|
|
|
//
|
|
// If the base file system doesn't provide a handler for this
|
|
// operation or the handler couldn't release the lock, perform the
|
|
// default action, which is releasing the ResourceToRelease.
|
|
//
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST &&
|
|
BaseFsFailedOperation) {
|
|
|
|
ExReleaseResourceLite( ResourceToRelease );
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Again, we only want to try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
FsRtlAcquireFileForCcFlush (
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine acquires a file system resource prior to a call to CcFlush.
|
|
|
|
This routine is obsolete --- FsRtlAcquireFileForCcFlushEx should
|
|
be used instead.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Just call the new version of this routine and ignore
|
|
// the return value. In the debug version, we will assert
|
|
// if we see a failure here to encourage people to call the
|
|
// FsRtlAcquireFileForCcFlushEx.
|
|
//
|
|
|
|
Status = FsRtlAcquireFileForCcFlushEx( FileObject );
|
|
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
FsRtlAcquireFileForCcFlushEx (
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine acquires a file system resource prior to a call to CcFlush.
|
|
This operation is presented to all the file system filters in the
|
|
filter stack for this volume. If all filters success the operation,
|
|
the base file system is requested to acquire the file system resource
|
|
for CcFlush.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PDEVICE_OBJECT BaseFsDeviceObject;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseFsDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireForCcFlush ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForCcFlush ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForCcFlush )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
//
|
|
// Call routine to initialize the control structure.
|
|
//
|
|
|
|
Status = FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_ACQUIRE_FOR_CC_FLUSH,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
TRUE );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// There are no operation specific parameters for this
|
|
// operation, so just perform the pre-callbacks.
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
TRUE,
|
|
TRUE,
|
|
&BaseFsFailedOperation );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have any filters to call, but we still need
|
|
// to disable APCs.
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseFsDeviceReference = TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForCcFlush))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireForCcFlush )) {
|
|
|
|
Status = FastIoDispatch->AcquireForCcFlush( FileObject,
|
|
BaseFsDeviceObject );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseFsDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST) );
|
|
|
|
//
|
|
// If the file system doesn't have a dispatch handler or failed this
|
|
// this operation, try to acquire the appropriate resources ourself.
|
|
//
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST &&
|
|
BaseFsFailedOperation) {
|
|
|
|
PFSRTL_COMMON_FCB_HEADER Header = FileObject->FsContext;
|
|
|
|
//
|
|
// If not already owned get the main resource exclusive because we may
|
|
// extend ValidDataLength. Otherwise acquire it one more time recursively.
|
|
//
|
|
|
|
if (Header->Resource != NULL) {
|
|
|
|
if (!ExIsResourceAcquiredSharedLite( Header->Resource )) {
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
|
|
} else {
|
|
|
|
ExAcquireResourceSharedLite( Header->Resource, TRUE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also get the paging I/O resource ahead of any MM resources.
|
|
//
|
|
|
|
if (Header->PagingIoResource != NULL) {
|
|
|
|
ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Again, we only want to call try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
|
|
//
|
|
// If this lock was not successfully acquired, then the lock
|
|
// will not need to be released. Therefore, we need to call
|
|
// FsRtlExitFileSystem now.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
gCounter.AcquireFileForCcFlushEx_Succeed ++;
|
|
|
|
} else {
|
|
|
|
gCounter.AcquireFileForCcFlushEx_Fail ++;
|
|
}
|
|
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
FsRtlReleaseFileForCcFlush (
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine releases a file system resource previously acquired for
|
|
the CcFlush.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT BaseFsDeviceObject, DeviceObject;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseFsDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
gCounter.ReleaseFileForCcFlush ++;
|
|
#endif
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, ReleaseForCcFlush ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForCcFlush ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForCcFlush )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_RELEASE_FOR_CC_FLUSH,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
FALSE );
|
|
|
|
//
|
|
// There are no operation-specific parameters to initialize,
|
|
// so perform the preoperation callbacks.
|
|
//
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
FALSE,
|
|
TRUE,
|
|
&BaseFsFailedOperation );
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseFsDeviceReference= TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForCcFlush ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForCcFlush ))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, ReleaseForCcFlush )) {
|
|
|
|
Status = FastIoDispatch->ReleaseForCcFlush( FileObject, BaseFsDeviceObject );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseFsDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST) );
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST &&
|
|
BaseFsFailedOperation) {
|
|
|
|
PFSRTL_COMMON_FCB_HEADER Header = FileObject->FsContext;
|
|
|
|
//
|
|
// The base file system doesn't provide a handler for this
|
|
// operation, so perform the default actions.
|
|
//
|
|
|
|
//
|
|
// Free whatever we could have acquired.
|
|
//
|
|
|
|
if (Header->PagingIoResource != NULL) {
|
|
|
|
ExReleaseResourceLite( Header->PagingIoResource );
|
|
}
|
|
|
|
if (Header->Resource != NULL) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT( Status == STATUS_SUCCESS );
|
|
|
|
//
|
|
// Again, we only want to call try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
FsRtlAcquireFileExclusive (
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by NtCreateSection to pre-acquire file system
|
|
resources in order to avoid deadlocks. If there is a FastIo entry
|
|
for AcquireFileForNtCreateSection then that routine will be called.
|
|
Otherwise, we will simply acquire the main file resource exclusive.
|
|
If there is no main resource then we acquire nothing and return
|
|
FALSE. In the cases that we acquire a resource, we also set the
|
|
TopLevelIrp field in the thread local storage to indicate to file
|
|
systems beneath us that we have acquired file system resources.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Just call the common version of this function,
|
|
// FsRtlAcquireFileExclusiveCommon.
|
|
//
|
|
|
|
Status = FsRtlAcquireFileExclusiveCommon( FileObject, SyncTypeOther, 0 );
|
|
|
|
//
|
|
// This should always be STATUS_SUCCESS since we are not
|
|
// allowing failures and the file system cannot fail
|
|
// this operation...
|
|
//
|
|
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
FsRtlAcquireToCreateMappedSection (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG SectionPageProtection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is meant to replace FsRtlAcquireFileExclusive for
|
|
the memory manager. Mm calls this routine to synchronize
|
|
for a mapped section create, but filters are allowed
|
|
to fail this operation. Other components that want to
|
|
synchronize with section creation should call
|
|
FsRtlAcquireFileExclusive.
|
|
|
|
This routine calls FsRtlAcquireFileExclusiveCommon to do
|
|
all the work.
|
|
|
|
This routine is used by NtCreateSection to pre-acquire file system
|
|
resources in order to avoid deadlocks. If there is a FastIo entry
|
|
for AcquireFileForNtCreateSection then that routine will be called.
|
|
Otherwise, we will simply acquire the main file resource exclusive.
|
|
If there is no main resource then we acquire nothing and return
|
|
FALSE. In the cases that we acquire a resource, we also set the
|
|
TopLevelIrp field in the thread local storage to indicate to file
|
|
systems beneath us that we have acquired file system resources.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
SectionPageProtection - The access requested for the section being
|
|
created.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
return FsRtlAcquireFileExclusiveCommon( FileObject, SyncTypeCreateSection, SectionPageProtection );
|
|
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
NTSTATUS
|
|
FsRtlAcquireFileExclusiveCommon (
|
|
IN PFILE_OBJECT FileObject,
|
|
IN FS_FILTER_SECTION_SYNC_TYPE SyncType,
|
|
IN ULONG SectionPageProtection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to pre-acquire file system resources in order
|
|
to avoid deadlocks. The file system filters for this volume
|
|
will be notified about this operation, then, if there is a FastIo
|
|
entry for AcquireFileForNtCreateSection, that routine will be called.
|
|
Otherwise, we will simply acquire the main file resource exclusive.
|
|
If there is no main resource then we acquire nothing and return
|
|
STATUS_SUCCESS. Finally, the file system filters will be notified
|
|
whether or not this resource has been acquired.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
CreatingMappedSection - TRUE if this lock is being acquired so that
|
|
a mapped section can be created. Filters are allowed
|
|
to fail this operation. FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_OBJECT DeviceObject, BaseFsDeviceObject;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN AllowFilterToFailOperation;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseFsDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireFileForNtCreateSection ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForSectionSynchronization ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForSectionSynchronization )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
//
|
|
// Initialize operation specific parameters for this
|
|
// operation.
|
|
//
|
|
|
|
FsFilterCtrl.Data.Parameters.AcquireForSectionSynchronization.SyncType =
|
|
SyncType;
|
|
FsFilterCtrl.Data.Parameters.AcquireForSectionSynchronization.PageProtection =
|
|
SectionPageProtection;
|
|
|
|
switch (SyncType) {
|
|
case SyncTypeCreateSection:
|
|
AllowFilterToFailOperation = TRUE;
|
|
break;
|
|
|
|
case SyncTypeOther:
|
|
default:
|
|
AllowFilterToFailOperation = FALSE;
|
|
}
|
|
|
|
//
|
|
// Call routine to initialize the control structure.
|
|
//
|
|
|
|
Status = FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
AllowFilterToFailOperation );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// There are no operation specific parameters for this
|
|
// operation, so just perform the pre-callbacks.
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
//
|
|
// Note: If the filter is allowed to fail the operation, so is the
|
|
// base file system, so we will just use that variable for both
|
|
// parameters to FsFilterPerformCallbacks.
|
|
//
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
AllowFilterToFailOperation,
|
|
AllowFilterToFailOperation,
|
|
&BaseFsFailedOperation );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have any filters to call, but we still need
|
|
// to disable APCs.
|
|
//
|
|
|
|
FsRtlEnterFileSystem();
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseFsDeviceReference = TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreAcquireForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostAcquireForSectionSynchronization ))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, AcquireFileForNtCreateSection )) {
|
|
|
|
FastIoDispatch->AcquireFileForNtCreateSection( FileObject );
|
|
|
|
//
|
|
// The status should already be STATUS_SUCCESS if we come down
|
|
// this path. Since the FastIo handler doesn't return a value
|
|
// the status should remain STATUS_SUCCESS.
|
|
//
|
|
|
|
// Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseFsDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST) );
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST &&
|
|
BaseFsFailedOperation) {
|
|
|
|
PFSRTL_COMMON_FCB_HEADER Header;
|
|
|
|
//
|
|
// The file system doesn't have a dispatch handler for this
|
|
// operation, so try to acquire the appropriate resources
|
|
// ourself.
|
|
//
|
|
|
|
//
|
|
// If there is a main file resource, acquire that.
|
|
//
|
|
|
|
Header = FileObject->FsContext;
|
|
|
|
if ((Header != NULL) &&
|
|
(Header->Resource != NULL)) {
|
|
|
|
ExAcquireResourceExclusiveLite( Header->Resource, TRUE );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Again, we only want to call try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
|
|
//
|
|
// If this lock was not successfully acquired, then the lock
|
|
// will not need to be released. Therefore, we need to call
|
|
// FsRtlExitFileSystem now.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
gCounter.AcquireFileExclusiveEx_Succeed ++;
|
|
|
|
} else {
|
|
|
|
gCounter.AcquireFileExclusiveEx_Fail ++;
|
|
}
|
|
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
FsRtlReleaseFile (
|
|
IN PFILE_OBJECT FileObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine releases resources acquired by FsRtlAcquireFileExclusive.
|
|
|
|
Arguments:
|
|
|
|
FileObject - Pointer to the file object being written.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT BaseFsDeviceObject, DeviceObject;
|
|
FS_FILTER_CTRL FsFilterCtrl;
|
|
PFS_FILTER_CTRL CallFilters = &FsFilterCtrl;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PFS_FILTER_CALLBACKS FsFilterCallbacks;
|
|
BOOLEAN BaseFsGetsFsFilterCallbacks = FALSE;
|
|
BOOLEAN ReleaseBaseFsDeviceReference = FALSE;
|
|
BOOLEAN BaseFsFailedOperation = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
gCounter.ReleaseFile ++;
|
|
#endif
|
|
|
|
//
|
|
// There are cases when the device that is the base fs device for
|
|
// this file object will register for the FsFilter callbacks instead of
|
|
// the legacy FastIo interfaces (DFS does this). It then can redirect
|
|
// these operations to another stack that could possibly have file system
|
|
// filter drivers correctly.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
BaseFsDeviceObject = IoGetBaseFileSystemDeviceObject( FileObject );
|
|
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
|
|
//
|
|
// The BaseFsDeviceObject should only support one of these interfaces --
|
|
// either the FastIoDispatch interface for the FsFilterCallbacks interface.
|
|
// If a device provides support for both interfaces, we will only use
|
|
// the FsFilterCallback interface.
|
|
//
|
|
|
|
ASSERT( !(VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch, ReleaseFileForNtCreateSection ) &&
|
|
(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForSectionSynchronization ))) );
|
|
|
|
if (VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForSectionSynchronization )) {
|
|
|
|
BaseFsGetsFsFilterCallbacks = TRUE;
|
|
}
|
|
|
|
if (DeviceObject == BaseFsDeviceObject &&
|
|
!BaseFsGetsFsFilterCallbacks) {
|
|
|
|
//
|
|
// There are no filters attached to this device and the base file system
|
|
// does not want these callbacks. This quick check allows us to bypass the
|
|
// logic to see if any filters are interested.
|
|
//
|
|
|
|
|
|
CallFilters = NULL;
|
|
}
|
|
|
|
if (CallFilters) {
|
|
|
|
FsFilterCtrlInit( &FsFilterCtrl,
|
|
FS_FILTER_RELEASE_FOR_SECTION_SYNCHRONIZATION,
|
|
DeviceObject,
|
|
BaseFsDeviceObject,
|
|
FileObject,
|
|
FALSE );
|
|
|
|
//
|
|
// There are no operation-specific parameters to initialize,
|
|
// so perform the preoperation callbacks.
|
|
//
|
|
|
|
Status = FsFilterPerformCallbacks( &FsFilterCtrl,
|
|
FALSE,
|
|
FALSE,
|
|
&BaseFsFailedOperation );
|
|
}
|
|
|
|
if (Status == STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY) {
|
|
|
|
//
|
|
// The filter/file system completed the operation, therefore we just need to
|
|
// call the completion callbacks for this operation. There is no need to try
|
|
// to call the base file system.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (NT_SUCCESS( Status )) {
|
|
|
|
if (CallFilters && FlagOn( FsFilterCtrl.Flags, FS_FILTER_CHANGED_DEVICE_STACKS )) {
|
|
|
|
BaseFsDeviceObject = IoGetDeviceAttachmentBaseRef( FsFilterCtrl.Data.DeviceObject );
|
|
ReleaseBaseFsDeviceReference = TRUE;
|
|
FastIoDispatch = GET_FAST_IO_DISPATCH( BaseFsDeviceObject );
|
|
FsFilterCallbacks = GET_FS_FILTER_CALLBACKS( BaseFsDeviceObject );
|
|
FileObject = FsFilterCtrl.Data.FileObject;
|
|
}
|
|
|
|
if (!(VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PreReleaseForSectionSynchronization ) ||
|
|
VALID_FS_FILTER_CALLBACK_HANDLER( FsFilterCallbacks, PostReleaseForSectionSynchronization ))) {
|
|
|
|
//
|
|
// Call the base file system.
|
|
//
|
|
|
|
if (VALID_FAST_IO_DISPATCH_HANDLER( FastIoDispatch,
|
|
ReleaseFileForNtCreateSection )) {
|
|
|
|
FastIoDispatch->ReleaseFileForNtCreateSection( FileObject );
|
|
|
|
//
|
|
// The status should already be STATUS_SUCCESS if we come down
|
|
// this path. Since the FastIo handler doesn't return a value
|
|
// the status should remain STATUS_SUCCESS.
|
|
//
|
|
|
|
// Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// If there is a failure at this point, we know that the failure
|
|
// was caused by the base file system.
|
|
//
|
|
|
|
BaseFsFailedOperation = TRUE;
|
|
}
|
|
|
|
if (ReleaseBaseFsDeviceReference) {
|
|
|
|
ObDereferenceObject( BaseFsDeviceObject );
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_INVALID_DEVICE_REQUEST ) );
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST &&
|
|
BaseFsFailedOperation) {
|
|
|
|
PFSRTL_COMMON_FCB_HEADER Header = FileObject->FsContext;
|
|
|
|
//
|
|
// The base file system doesn't provide a handler for this
|
|
// operation, so perform the default actions.
|
|
//
|
|
|
|
//
|
|
// If there is a main file resource, release that.
|
|
//
|
|
|
|
if ((Header != NULL) && (Header->Resource != NULL)) {
|
|
|
|
ExReleaseResourceLite( Header->Resource );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Again, we only want to call try to do completion callbacks
|
|
// if there are any filters attached to this device that have
|
|
// completion callbacks. In any case, if we called down to the filters
|
|
// we need to free the FsFilterCtrl.
|
|
//
|
|
|
|
if (CallFilters) {
|
|
|
|
if (FS_FILTER_HAVE_COMPLETIONS( CallFilters )) {
|
|
|
|
FsFilterPerformCompletionCallbacks( &FsFilterCtrl, Status );
|
|
}
|
|
|
|
FsFilterCtrlFree( &FsFilterCtrl );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FsRtlGetFileSize(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT PLARGE_INTEGER FileSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to call the File System to get the FileSize
|
|
for a file.
|
|
|
|
It does this without acquiring the file object lock on synchronous file
|
|
objects. This routine is therefore safe to call if you already own
|
|
file system resources, while IoQueryFileInformation could (and does)
|
|
lead to deadlocks.
|
|
|
|
Arguments:
|
|
|
|
FileObject - The file to query
|
|
FileSize - Receives the file size.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The final I/O status of the operation. If the FileObject
|
|
refers to a directory, STATUS_FILE_IS_A_DIRECTORY is returned.
|
|
|
|
--*/
|
|
{
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
FILE_STANDARD_INFORMATION FileInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the address of the target device object.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
|
|
//
|
|
// Try the fast query call if it exists.
|
|
//
|
|
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
if (FastIoDispatch &&
|
|
FastIoDispatch->FastIoQueryStandardInfo &&
|
|
FastIoDispatch->FastIoQueryStandardInfo( FileObject,
|
|
TRUE,
|
|
&FileInformation,
|
|
&IoStatus,
|
|
DeviceObject )) {
|
|
//
|
|
// Cool, it worked.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Life's tough, take the long path.
|
|
//
|
|
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
BOOLEAN HardErrorState;
|
|
|
|
//
|
|
// Initialize the event.
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Allocate an I/O Request Packet (IRP) for this in-page operation.
|
|
//
|
|
|
|
Irp = IoAllocateIrp( DeviceObject->StackSize, FALSE );
|
|
if (Irp == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Disable hard errors over this call. Caller owns resources, is in a critical
|
|
// region and cannot complete hard error APCs.
|
|
//
|
|
|
|
HardErrorState = IoSetThreadHardErrorMode( FALSE );
|
|
|
|
//
|
|
// Get a pointer to the first stack location in the packet. This location
|
|
// will be used to pass the function codes and parameters to the first
|
|
// driver.
|
|
//
|
|
|
|
IrpSp = IoGetNextIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Fill in the IRP according to this request, setting the flags to
|
|
// just cause IO to set the event and deallocate the Irp.
|
|
//
|
|
|
|
Irp->Flags = IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->UserIosb = &IoStatus;
|
|
Irp->UserEvent = &Event;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->AssociatedIrp.SystemBuffer = &FileInformation;
|
|
|
|
//
|
|
// Fill in the normal query parameters.
|
|
//
|
|
|
|
IrpSp->MajorFunction = IRP_MJ_QUERY_INFORMATION;
|
|
IrpSp->FileObject = FileObject;
|
|
IrpSp->DeviceObject = DeviceObject;
|
|
IrpSp->Parameters.SetFile.Length = sizeof(FILE_STANDARD_INFORMATION);
|
|
IrpSp->Parameters.SetFile.FileInformationClass = FileStandardInformation;
|
|
|
|
//
|
|
// Queue the packet to the appropriate driver based. This routine
|
|
// should not raise.
|
|
//
|
|
|
|
Status = IoCallDriver( DeviceObject, Irp );
|
|
|
|
//
|
|
// If pending is returned (which is a successful status),
|
|
// we must wait for the request to complete.
|
|
//
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject( &Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER)NULL);
|
|
}
|
|
|
|
//
|
|
// If we got an error back in Status, then the Iosb
|
|
// was not written, so we will just copy the status
|
|
// there, then test the final status after that.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IoStatus.Status = Status;
|
|
}
|
|
|
|
//
|
|
// Reset the hard error state.
|
|
//
|
|
|
|
IoSetThreadHardErrorMode( HardErrorState );
|
|
}
|
|
|
|
//
|
|
// If the call worked, check to make sure it wasn't a directory and
|
|
// if not, fill in the FileSize parameter.
|
|
//
|
|
|
|
if (NT_SUCCESS(IoStatus.Status)) {
|
|
|
|
if (FileInformation.Directory) {
|
|
|
|
//
|
|
// Can't get file size for a directory. Return error.
|
|
//
|
|
|
|
IoStatus.Status = STATUS_FILE_IS_A_DIRECTORY;
|
|
|
|
} else {
|
|
|
|
*FileSize = FileInformation.EndOfFile;
|
|
}
|
|
}
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FsRtlSetFileSize(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT PLARGE_INTEGER FileSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to call the File System to update FileSize
|
|
for a file.
|
|
|
|
It does this without acquiring the file object lock on synchronous file
|
|
objects. This routine is therefore safe to call if you already own
|
|
file system resources, while IoSetInformation could (and does) lead
|
|
to deadlocks.
|
|
|
|
Arguments:
|
|
|
|
FileObject - A pointer to a referenced file object.
|
|
|
|
ValidDataLength - Pointer to new FileSize.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
NTSTATUS Status;
|
|
FILE_END_OF_FILE_INFORMATION Buffer;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
KEVENT Event;
|
|
PIRP Irp;
|
|
BOOLEAN HardErrorState;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy FileSize to our buffer.
|
|
//
|
|
|
|
Buffer.EndOfFile = *FileSize;
|
|
|
|
//
|
|
// Initialize the event.
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Begin by getting a pointer to the device object that the file resides
|
|
// on.
|
|
//
|
|
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
|
|
//
|
|
// Allocate an I/O Request Packet (IRP) for this in-page operation.
|
|
//
|
|
|
|
Irp = IoAllocateIrp( DeviceObject->StackSize, FALSE );
|
|
if (Irp == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Disable hard errors over this call. Caller owns resources, is in a critical
|
|
// region and cannot complete hard error APCs.
|
|
//
|
|
|
|
HardErrorState = IoSetThreadHardErrorMode( FALSE );
|
|
|
|
//
|
|
// Get a pointer to the first stack location in the packet. This location
|
|
// will be used to pass the function codes and parameters to the first
|
|
// driver.
|
|
//
|
|
|
|
IrpSp = IoGetNextIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Fill in the IRP according to this request, setting the flags to
|
|
// just cause IO to set the event and deallocate the Irp.
|
|
//
|
|
|
|
Irp->Flags = IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->UserIosb = &IoStatus;
|
|
Irp->UserEvent = &Event;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->AssociatedIrp.SystemBuffer = &Buffer;
|
|
|
|
//
|
|
// Fill in the normal set file parameters.
|
|
//
|
|
|
|
IrpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
|
|
IrpSp->FileObject = FileObject;
|
|
IrpSp->DeviceObject = DeviceObject;
|
|
IrpSp->Parameters.SetFile.Length = sizeof(FILE_END_OF_FILE_INFORMATION);
|
|
IrpSp->Parameters.SetFile.FileInformationClass = FileEndOfFileInformation;
|
|
|
|
//
|
|
// Queue the packet to the appropriate driver based on whether or not there
|
|
// is a VPB associated with the device. This routine should not raise.
|
|
//
|
|
|
|
Status = IoCallDriver( DeviceObject, Irp );
|
|
|
|
//
|
|
// If pending is returned (which is a successful status),
|
|
// we must wait for the request to complete.
|
|
//
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
KeWaitForSingleObject( &Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(PLARGE_INTEGER)NULL);
|
|
}
|
|
|
|
//
|
|
// If we got an error back in Status, then the Iosb
|
|
// was not written, so we will just copy the status
|
|
// there, then test the final status after that.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
IoStatus.Status = Status;
|
|
}
|
|
|
|
//
|
|
// Reset the hard error state.
|
|
//
|
|
|
|
IoSetThreadHardErrorMode( HardErrorState );
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
FsRtlIncrementCcFastReadNotPossible( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the CcFastReadNotPossible counter in the PRCB
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
HOT_STATISTIC( CcFastReadNotPossible ) += 1;
|
|
}
|
|
|
|
|
|
VOID
|
|
FsRtlIncrementCcFastReadWait( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the CcFastReadWait counter in the PRCB
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HOT_STATISTIC(CcFastReadWait) += 1;
|
|
}
|
|
|
|
|
|
VOID
|
|
FsRtlIncrementCcFastReadNoWait( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the CcFastReadNoWait counter in the PRCB
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
HOT_STATISTIC(CcFastReadNoWait) += 1;
|
|
}
|
|
|
|
|
|
VOID
|
|
FsRtlIncrementCcFastReadResourceMiss( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the CcFastReadResourceMiss
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
CcFastReadResourceMiss += 1;
|
|
}
|
|
|