1605 lines
49 KiB
C
1605 lines
49 KiB
C
|
/*++
|
|||
|
|
|||
|
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 );
|
|||
|
}
|
|||
|
}
|