NT4/private/ntos/cntfs/fstiosup.c
2020-09-30 17:12:29 +02:00

1605 lines
49 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
FstIoSup.c
Abstract:
This module implements the fast I/O routines for Ntfs.
Author:
Tom Miller [TomM] 16-May-96
Revision History:
--*/
#include "NtfsProc.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsCopyReadA)
#pragma alloc_text(PAGE, NtfsCopyWriteA)
#pragma alloc_text(PAGE, NtfsMdlReadA)
#pragma alloc_text(PAGE, NtfsPrepareMdlWriteA)
#pragma alloc_text(PAGE, NtfsWaitForIoAtEof)
#pragma alloc_text(PAGE, NtfsFinishIoAtEof)
#endif
BOOLEAN
NtfsCopyReadA (
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_ADVANCED_FCB_HEADER Header;
LARGE_INTEGER BeyondLastByte;
PDEVICE_OBJECT targetVdo;
EOF_WAIT_BLOCK EofWaitBlock;
BOOLEAN Status = TRUE;
ULONG PageCount = COMPUTE_PAGES_SPANNED(((PVOID)FileOffset->LowPart), Length);
BOOLEAN DoingIoAtEof = FALSE;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
//
// Special case a read of zero length
//
if (Length != 0) {
//
// Get a real pointer to the common fcb header
//
BeyondLastByte.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
Header = (PFSRTL_ADVANCED_FCB_HEADER)FileObject->FsContext;
//
// Enter the file system
//
FsRtlEnterFileSystem();
//
// Make our best guess on whether we need the file exclusive
// or shared. Note that we do not check FileOffset->HighPart
// until below.
//
if ((Header->PagingIoResource == NULL) ||
!ExAcquireResourceShared(Header->PagingIoResource, Wait)) {
Status = FALSE;
goto Done2;
}
HOT_STATISTIC(CcFastReadWait) += 1;
//
// Now synchronize with the FsRtl Header
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now see if we are reading beyond ValidDataLength. We have to
// do it now so that our reads are not nooped.
//
if (BeyondLastByte.QuadPart > Header->ValidDataLength.QuadPart) {
//
// We must serialize with anyone else doing I/O at beyond
// ValidDataLength, and then remember if we need to declare
// when we are done.
//
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
NtfsWaitForIoAtEof( Header, FileOffset, Length, &EofWaitBlock );
//
// Set the Flag if we are in fact beyond ValidDataLength.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
}
}
ExReleaseFastMutex( Header->FastMutex );
//
// 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)) {
HOT_STATISTIC(CcFastReadNotPossible) += 1;
Status = FALSE;
goto Done;
}
//
// 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.
//
HOT_STATISTIC(CcFastReadNotPossible) += 1;
Status = FALSE;
goto Done;
}
}
//
// 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;
goto Done;
}
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) ||
((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;
Done: NOTHING;
if (DoingIoAtEof) {
ExAcquireFastMutex( Header->FastMutex );
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
ExReleaseResource( Header->PagingIoResource );
Done2: NOTHING;
FsRtlExitFileSystem();
} else {
//
// A zero length transfer was requested.
//
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = 0;
}
return Status;
}
BOOLEAN
NtfsCopyWriteA (
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_ADVANCED_FCB_HEADER Header;
EOF_WAIT_BLOCK EofWaitBlock;
LARGE_INTEGER Offset;
LARGE_INTEGER NewFileSize;
LARGE_INTEGER OldFileSize;
PDEVICE_OBJECT targetVdo = IoGetRelatedDeviceObject( FileObject );
PFAST_IO_DISPATCH FastIoDispatch = targetVdo->DriverObject->FastIoDispatch;
ULONG DoingIoAtEof = FALSE;
BOOLEAN Status = TRUE;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
//
// Get a real pointer to the common fcb header
//
Header = (PFSRTL_ADVANCED_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) &&
(Header->PagingIoResource != NULL)) {
//
// 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 mad to
// this path also be applied to the 64-bit path
// which is the else of this test!
//
NewFileSize.QuadPart = FileOffset->QuadPart + Length;
Offset = *FileOffset;
if (Wait && (Header->AllocationSize.HighPart == 0)) {
//
// Prevent truncates by acquiring paging I/O
//
ExAcquireResourceShared( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now see if we will change FileSize. We have to do it now
// so that our reads are not nooped.
//
if ((FileOffset->HighPart < 0) || (NewFileSize.LowPart > Header->ValidDataLength.LowPart)) {
//
// We can change FileSize and ValidDataLength if either, no one
// else is now, or we are still extending after waiting.
//
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
NtfsWaitForIoAtEof( Header, FileOffset, Length, &EofWaitBlock );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
//
// Now that we are synchronized for end of file cases,
// we can calculate the real offset for this transfer and
// the new file size (if we succeed).
//
if ((FileOffset->HighPart < 0)) {
Offset = Header->FileSize;
}
//
// Above we allowed any negative .HighPart for the 32-bit path,
// but now we are counting on the I/O system to have thrown
// any negative number other than write to end of file.
//
ASSERT(Offset.HighPart >= 0);
//
// Now calculate the new FileSize and see if we wrapped the
// 32-bit boundary.
//
NewFileSize.QuadPart = Offset.QuadPart + Length;
//
// Update Filesize now so that we do not truncate reads.
//
OldFileSize.QuadPart = Header->FileSize.QuadPart;
if (NewFileSize.QuadPart > Header->FileSize.QuadPart) {
//
// If we are beyond AllocationSize, make sure we will
// ErrOut below, and don't modify FileSize now!
//
if (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) {
NewFileSize.QuadPart = (LONGLONG)0x7FFFFFFFFFFFFFFF;
} else {
Header->FileSize.QuadPart = NewFileSize.QuadPart;
}
}
}
}
ExReleaseFastMutex( Header->FastMutex );
//
// 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) ||
/* Remove? */ (NewFileSize.LowPart > Header->AllocationSize.QuadPart) ||
(Offset.LowPart >= (Header->ValidDataLength.LowPart + 0x2000)) ||
(NewFileSize.HighPart != 0)) {
goto ErrOut;
}
//
// Check if fast I/O is questionable and if so then go ask
// the file system the answer
//
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
targetVdo = IoGetRelatedDeviceObject( FileObject );
FastIoDispatch = targetVdo->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,
&Offset,
Length,
TRUE,
LockKey,
FALSE, // write operation
IoStatus,
targetVdo )) {
//
// Fast I/O is not possible so cleanup and return.
//
goto ErrOut;
}
}
//
// 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.LowPart > Header->ValidDataLength.LowPart) {
CcZeroData( FileObject,
&Header->ValidDataLength,
&Offset,
TRUE );
}
CcFastCopyWrite( FileObject,
Offset.LowPart,
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) {
//
// Set this handle as having modified the file and update
// the current file position pointer
//
FileObject->Flags |= FO_FILE_MODIFIED;
FileObject->CurrentByteOffset.QuadPart = Offset.QuadPart + Length;
if (DoingIoAtEof) {
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it).
//
CcGetFileSizePointer(FileObject)->LowPart = Header->FileSize.LowPart;
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
ExAcquireFastMutex( Header->FastMutex );
Header->ValidDataLength = NewFileSize;
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
goto Done1;
}
//
// Here is the 64-bit or no-wait path.
//
} else {
//
// Prevent truncates by acquiring paging I/O
//
Status = ExAcquireResourceShared( Header->PagingIoResource, Wait );
if (!Status) {
goto Done2;
}
//
// Now synchronize with the FsRtl Header
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now see if we will change FileSize. We have to do it now
// so that our reads are not nooped.
//
if ((FileOffset->QuadPart < 0) || (NewFileSize.QuadPart > Header->ValidDataLength.QuadPart)) {
//
// We can change FileSize and ValidDataLength if either, no one
// else is now, or we are still extending after waiting.
//
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
NtfsWaitForIoAtEof( Header, FileOffset, Length, &EofWaitBlock );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
//
// Now that we are synchronized for end of file cases,
// we can calculate the real offset for this transfer and
// the new file size (if we succeed).
//
if ((FileOffset->QuadPart < 0)) {
Offset = Header->FileSize;
}
//
// Now calculate the new FileSize and see if we wrapped the
// 32-bit boundary.
//
NewFileSize.QuadPart = Offset.QuadPart + Length;
//
// Update Filesize now so that we do not truncate reads.
//
OldFileSize.QuadPart = Header->FileSize.QuadPart;
if (NewFileSize.QuadPart > Header->FileSize.QuadPart) {
//
// If we are beyond AllocationSize, make sure we will
// ErrOut below, and don't modify FileSize now!
//
if (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) {
NewFileSize.QuadPart = (LONGLONG)0x7FFFFFFFFFFFFFFF;
} else {
Header->FileSize.QuadPart = NewFileSize.QuadPart;
}
}
}
}
ExReleaseFastMutex( Header->FastMutex );
//
// 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.
//
if ((FileObject->PrivateCacheMap == NULL) ||
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
/* Remove? */ (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) ||
(Offset.QuadPart >= (Header->ValidDataLength.QuadPart + 0x2000))) {
goto ErrOut;
}
//
// Check if fast I/O is questionable and if so then go ask
// the file system the answer
//
if (Header->IsFastIoPossible == FastIoIsQuestionable) {
targetVdo = IoGetRelatedDeviceObject( FileObject );
FastIoDispatch = targetVdo->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,
&Offset,
Length,
Wait,
LockKey,
FALSE, // write operation
IoStatus,
targetVdo )) {
//
// Fast I/O is not possible so cleanup and return.
//
goto ErrOut;
}
}
//
// 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 ValidDataLength.
//
if (Status) {
//
// Set this handle as having modified the file and update
// the current file position pointer
//
FileObject->Flags |= FO_FILE_MODIFIED;
FileObject->CurrentByteOffset.QuadPart = Offset.QuadPart + Length;
if (DoingIoAtEof) {
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it).
//
CcGetFileSizePointer(FileObject)->QuadPart = Header->FileSize.QuadPart;
ExAcquireFastMutex( Header->FastMutex );
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
Header->ValidDataLength = NewFileSize;
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
goto Done1;
}
}
ErrOut: NOTHING;
Status = FALSE;
if (DoingIoAtEof) {
ExAcquireFastMutex( Header->FastMutex );
Header->FileSize = OldFileSize;
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
Done1: ExReleaseResource( Header->PagingIoResource );
Done2: FsRtlExitFileSystem();
}
} else {
//
// We could not do the I/O now.
//
Status = FALSE;
}
return Status;
}
BOOLEAN
NtfsMdlReadA (
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.
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_ADVANCED_FCB_HEADER Header;
EOF_WAIT_BLOCK EofWaitBlock;
BOOLEAN DoingIoAtEof = FALSE;
BOOLEAN Status = TRUE;
LARGE_INTEGER BeyondLastByte;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
//
// Special case a read of zero length
//
if (Length == 0) {
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = 0;
//
// Get a real pointer to the common fcb header
//
} else {
BeyondLastByte.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
Header = (PFSRTL_ADVANCED_FCB_HEADER)FileObject->FsContext;
//
// Enter the file system
//
FsRtlEnterFileSystem();
*(PULONG)CcFastMdlReadWait += 1;
//
// Acquired shared on the common fcb header
//
if (Header->PagingIoResource == NULL) {
Status = FALSE;
goto Done2;
}
(VOID)ExAcquireResourceShared( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now see if we are reading beyond ValidDataLength. We have to
// do it now so that our reads are not nooped.
//
if (BeyondLastByte.QuadPart > Header->ValidDataLength.QuadPart) {
//
// We must serialize with anyone else doing I/O at beyond
// ValidDataLength, and then remember if we need to declare
// when we are done.
//
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
NtfsWaitForIoAtEof( Header, FileOffset, Length, &EofWaitBlock );
//
// Set the Flag if we are in fact beyond ValidDataLength.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
}
}
ExReleaseFastMutex( Header->FastMutex );
//
// 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)) {
Status = FALSE;
goto Done;
}
//
// 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.
//
Status = FALSE;
goto Done;
}
}
//
// 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;
goto Done;
}
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;
Done: NOTHING;
if (DoingIoAtEof) {
ExAcquireFastMutex( Header->FastMutex );
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
ExReleaseResource( Header->PagingIoResource );
Done2: NOTHING;
FsRtlExitFileSystem();
}
return Status;
}
BOOLEAN
NtfsPrepareMdlWriteA (
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.
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_ADVANCED_FCB_HEADER Header;
EOF_WAIT_BLOCK EofWaitBlock;
LARGE_INTEGER Offset, NewFileSize;
LARGE_INTEGER OldFileSize;
ULONG DoingIoAtEof = FALSE;
BOOLEAN Status = TRUE;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
//
// Get a real pointer to the common fcb header
//
Header = (PFSRTL_ADVANCED_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, TRUE, FALSE ) &&
!FlagOn(FileObject->Flags, FO_WRITE_THROUGH) &&
CcCopyWriteWontFlush(FileObject, FileOffset, Length) &&
(Header->PagingIoResource != NULL)) {
//
// Assume our transfer will work
//
IoStatus->Status = STATUS_SUCCESS;
//
// Special case the zero byte length
//
if (Length != 0) {
//
// Enter the file system
//
FsRtlEnterFileSystem();
//
// Make our best guess on whether we need the file exclusive or
// shared.
//
NewFileSize.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
Offset = *FileOffset;
//
// Prevent truncates by acquiring paging I/O
//
ExAcquireResourceShared( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now see if we will change FileSize. We have to do it now
// so that our reads are not nooped.
//
if ((FileOffset->QuadPart < 0) || (NewFileSize.QuadPart > Header->ValidDataLength.QuadPart)) {
//
// We can change FileSize and ValidDataLength if either, no one
// else is now, or we are still extending after waiting.
//
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
NtfsWaitForIoAtEof( Header, FileOffset, Length, &EofWaitBlock );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
//
// Now that we are synchronized for end of file cases,
// we can calculate the real offset for this transfer and
// the new file size (if we succeed).
//
if ((FileOffset->QuadPart < 0)) {
Offset = Header->FileSize;
}
//
// Now calculate the new FileSize and see if we wrapped the
// 32-bit boundary.
//
NewFileSize.QuadPart = Offset.QuadPart + Length;
//
// Update Filesize now so that we do not truncate reads.
//
OldFileSize.QuadPart = Header->FileSize.QuadPart;
if (NewFileSize.QuadPart > Header->FileSize.QuadPart) {
//
// If we are beyond AllocationSize, make sure we will
// ErrOut below, and don't modify FileSize now!
//
if (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) {
NewFileSize.QuadPart = (LONGLONG)0x7FFFFFFFFFFFFFFF;
} else {
Header->FileSize.QuadPart = NewFileSize.QuadPart;
}
}
}
}
ExReleaseFastMutex( Header->FastMutex );
//
// 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.
//
if ((FileObject->PrivateCacheMap == NULL) ||
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
/* Remove? */ (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) ||
(Offset.QuadPart >= (Header->ValidDataLength.QuadPart + 0x2000))) {
goto ErrOut;
}
//
// 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,
&Offset,
Length,
TRUE,
LockKey,
FALSE, // write operation
IoStatus,
IoGetRelatedDeviceObject( FileObject ) )) {
//
// Fast I/O is not possible so release the Fcb and return.
//
goto ErrOut;
}
}
//
// 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 ValidDataLength.
//
if (Status) {
//
// Set this handle as having modified the file
//
FileObject->Flags |= FO_FILE_MODIFIED;
IoStatus->Information = Length;
if (DoingIoAtEof) {
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it).
//
CcGetFileSizePointer(FileObject)->QuadPart = Header->FileSize.QuadPart;
ExAcquireFastMutex( Header->FastMutex );
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
Header->ValidDataLength = NewFileSize;
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
goto Done1;
}
ErrOut: NOTHING;
Status = FALSE;
if (DoingIoAtEof) {
ExAcquireFastMutex( Header->FastMutex );
Header->FileSize = OldFileSize;
NtfsFinishIoAtEof( Header );
ExReleaseFastMutex( Header->FastMutex );
}
Done1: ExReleaseResource( Header->PagingIoResource );
FsRtlExitFileSystem();
}
} else {
//
// We could not do the I/O now.
//
Status = FALSE;
}
return Status;
}
BOOLEAN
NtfsWaitForIoAtEof (
IN PFSRTL_ADVANCED_FCB_HEADER Header,
IN OUT PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN PEOF_WAIT_BLOCK EofWaitBlock
)
/*++
Routine Description:
This routine may be called while synchronized for cached write, to
test for a possible Eof update, and return with a status if Eof is
being updated and with the previous FileSize to restore on error.
All updates to Eof are serialized by waiting in this routine. If
this routine returns TRUE, then NtfsFinishIoAtEof must be called.
This routine must be called while synchronized with the FsRtl header.
Arguments:
Header - Pointer to the FsRtl header for the file
FileOffset - Pointer to FileOffset for the intended write
Length - Length for the intended write
EofWaitBlock - Uninitialized structure used only to serialize Eof updates
Return Value:
FALSE - If the write does not extend Eof (OldFileSize not returned)
TRUE - If the write does extend Eof OldFileSize returned and caller
must eventually call NtfsFinishIoAtEof
--*/
{
PAGED_CODE();
ASSERT( Header->FileSize.QuadPart >= Header->ValidDataLength.QuadPart );
//
// Initialize the event and queue our block
//
KeInitializeEvent( &EofWaitBlock->Event, NotificationEvent, FALSE );
InsertTailList( Header->PendingEofAdvances, &EofWaitBlock->EofWaitLinks );
//
// Free the mutex and wait
//
ExReleaseFastMutex( Header->FastMutex );
KeWaitForSingleObject( &EofWaitBlock->Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
//
// Now, resynchronize and get on with it.
//
ExAcquireFastMutex( Header->FastMutex );
//
// Now we have to check again, and actually catch the case
// where we are no longer extending!
//
if ((FileOffset->QuadPart >= 0) &&
((FileOffset->QuadPart + Length) <= Header->ValidDataLength.QuadPart)) {
NtfsFinishIoAtEof( Header );
return FALSE;
}
return TRUE;
}
VOID
NtfsFinishIoAtEof (
IN PFSRTL_ADVANCED_FCB_HEADER Header
)
/*++
Routine Description:
This routine must be called if NtfsWaitForIoAtEof returned
TRUE, or we otherwise set EOF_ADVANCE_ACTIVE.
This routine must be called while synchronized with the FsRtl header.
Arguments:
Header - Pointer to the FsRtl header for the file
Return Value:
None
--*/
{
PEOF_WAIT_BLOCK EofWaitBlock;
PAGED_CODE();
//
// If anyone is waiting, just let them go.
//
if (!IsListEmpty(Header->PendingEofAdvances)) {
EofWaitBlock = (PEOF_WAIT_BLOCK)RemoveHeadList( Header-> PendingEofAdvances );
KeSetEvent( &EofWaitBlock->Event, 0, FALSE );
//
// Otherwise, show there is no active extender now.
//
} else {
ClearFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
}
}