windows-nt/Source/XPSP1/NT/base/fs/ntfs/fstiosup.c

2366 lines
77 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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
#ifdef NTFS_RWC_DEBUG
PRWC_HISTORY_ENTRY
NtfsGetHistoryEntry (
IN PSCB Scb
);
#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
--*/
{
PNTFS_ADVANCED_FCB_HEADER Header;
LARGE_INTEGER BeyondLastByte;
PDEVICE_OBJECT targetVdo;
#ifdef COMPRESS_ON_WIRE
PCOMPRESSION_SYNC CompressionSync = NULL;
#endif
BOOLEAN WasDataRead = TRUE;
ULONG PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES( FileOffset->QuadPart, Length );
BOOLEAN DoingIoAtEof = FALSE;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
#ifdef NTFS_NO_FASTIO
UNREFERENCED_PARAMETER( FileObject );
UNREFERENCED_PARAMETER( FileOffset );
UNREFERENCED_PARAMETER( Length );
UNREFERENCED_PARAMETER( Wait );
UNREFERENCED_PARAMETER( LockKey );
UNREFERENCED_PARAMETER( Buffer );
UNREFERENCED_PARAMETER( IoStatus );
return FALSE;
#endif
//
// Don't take the fast io path if someone is already active in this thread.
//
if (IoGetTopLevelIrp() != NULL) {
return FALSE;
}
//
// Special case a read of zero length
//
if (Length != 0) {
//
// Get a real pointer to the common fcb header. Check for overflow.
//
if (MAXLONGLONG - FileOffset->QuadPart < (LONGLONG)Length) {
return FALSE;
}
BeyondLastByte.QuadPart = FileOffset->QuadPart + (LONGLONG)Length;
Header = (PNTFS_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 (Wait) {
FsRtlIncrementCcFastReadWait();
} else {
FsRtlIncrementCcFastReadNoWait();
}
if ((Header->PagingIoResource == NULL) ||
!ExAcquireResourceSharedLite(Header->PagingIoResource, Wait)) {
FsRtlIncrementCcFastReadResourceMiss();
WasDataRead = FALSE;
goto Done2;
}
//
// Now synchronize with the FsRtl Header
//
NtfsAcquireFsrtlHeader( (PSCB)Header );
//
// 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 );
//
// Set the Flag if we are in fact beyond ValidDataLength.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
} else {
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
#endif
}
}
NtfsReleaseFsrtlHeader( (PSCB)Header );
//
// 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)) {
FsRtlIncrementCcFastReadNotPossible();
WasDataRead = 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;
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.
//
FsRtlIncrementCcFastReadNotPossible();
WasDataRead = 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
//
IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
try {
//
// If there is a compressed section, then synchronize with that cache.
//
IoStatus->Status = STATUS_SUCCESS;
#ifdef COMPRESS_ON_WIRE
//
// If there is a compressed section, then we have to synchronize with
// the data out there. Note the FileObjectC better also be there, or else
// we would have made the fast I/O not possible.
//
if (((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
LONGLONG LocalOffset = FileOffset->QuadPart;
ULONG LocalLength;
ULONG LengthLeft = Length;
ASSERT(Header->FileObjectC != NULL);
//
// If we are doing DoingIoAtEof then take the long path. Otherwise a recursive
// flush will try to reacquire DoingIoAtEof and deadlock.
//
if (DoingIoAtEof) {
WasDataRead = FALSE;
} else {
do {
ULONG ViewOffset;
//
// Calculate length left in view.
//
ViewOffset = ((ULONG) LocalOffset & (VACB_MAPPING_GRANULARITY - 1));
LocalLength = LengthLeft;
if (LocalLength > VACB_MAPPING_GRANULARITY - ViewOffset) {
LocalLength = VACB_MAPPING_GRANULARITY - ViewOffset;
}
//
// Trim the read so we don't inadvertently go beyond the end of the
// view because of the MM read ahead.
//
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(((PVOID)(ULONG_PTR)((ULONG)LocalOffset)), LocalLength);
if (LocalLength > (VACB_MAPPING_GRANULARITY - ((PageCount - 1) * PAGE_SIZE) - ViewOffset)) {
#ifdef NTFS_RWC_DEBUG
{
PRWC_HISTORY_ENTRY NextBuffer;
NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
NextBuffer->Operation = TrimCopyRead;
NextBuffer->Information = PageCount;
NextBuffer->FileOffset = (ULONG) LocalOffset;
NextBuffer->Length = (ULONG) LocalLength;
}
#endif
LocalLength = (VACB_MAPPING_GRANULARITY - ((PageCount - 1) * PAGE_SIZE) - ViewOffset);
PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(((PVOID)(ULONG_PTR)((ULONG)LocalOffset)), LocalLength);
ASSERT( LocalLength <= (VACB_MAPPING_GRANULARITY - ((PageCount - 1) * PAGE_SIZE) - ViewOffset) );
}
IoStatus->Status = NtfsSynchronizeUncompressedIo( (PSCB)Header,
&LocalOffset,
LocalLength,
FALSE,
&CompressionSync );
if (NT_SUCCESS(IoStatus->Status)) {
if (Wait && ((BeyondLastByte.HighPart | Header->FileSize.HighPart) == 0)) {
CcFastCopyRead( FileObject,
(ULONG)LocalOffset,
LocalLength,
PageCount,
Buffer,
IoStatus );
ASSERT( (IoStatus->Status == STATUS_END_OF_FILE) ||
((FileOffset->LowPart + IoStatus->Information) <= Header->FileSize.LowPart));
} else {
WasDataRead = CcCopyRead( FileObject,
(PLARGE_INTEGER)&LocalOffset,
LocalLength,
Wait,
Buffer,
IoStatus );
ASSERT( !WasDataRead || (IoStatus->Status == STATUS_END_OF_FILE) ||
((LocalOffset + (LONG_PTR) IoStatus->Information) <= Header->FileSize.QuadPart));
}
LocalOffset += LocalLength;
LengthLeft -= LocalLength;
Buffer = Add2Ptr( Buffer, LocalLength );
}
} while ((LengthLeft != 0) && WasDataRead && NT_SUCCESS(IoStatus->Status));
//
// Remember the full amount of the read.
//
if (WasDataRead) {
IoStatus->Information = Length;
}
}
} else {
#endif
if (Wait && ((BeyondLastByte.HighPart | Header->FileSize.HighPart) == 0)) {
CcFastCopyRead( FileObject,
FileOffset->LowPart,
Length,
PageCount,
Buffer,
IoStatus );
ASSERT( (IoStatus->Status == STATUS_END_OF_FILE) ||
((FileOffset->LowPart + IoStatus->Information) <= Header->FileSize.LowPart));
} else {
WasDataRead = CcCopyRead( FileObject,
FileOffset,
Length,
Wait,
Buffer,
IoStatus );
ASSERT( !WasDataRead || (IoStatus->Status == STATUS_END_OF_FILE) ||
((FileOffset->QuadPart + (LONG_PTR) IoStatus->Information) <= Header->FileSize.QuadPart));
}
#ifdef COMPRESS_ON_WIRE
}
#endif
FileObject->Flags |= FO_FILE_FAST_IO_READ;
if (WasDataRead) {
FileObject->CurrentByteOffset.QuadPart = FileOffset->QuadPart + IoStatus->Information;
}
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
WasDataRead = FALSE;
}
IoSetTopLevelIrp( NULL );
#ifdef COMPRESS_ON_WIRE
if (CompressionSync != NULL) {
NtfsReleaseCompressionSync( CompressionSync );
}
#endif
Done:
if (DoingIoAtEof) {
FsRtlUnlockFsRtlHeader( Header );
}
ExReleaseResourceLite( Header->PagingIoResource );
Done2:
FsRtlExitFileSystem();
} else {
//
// A zero length transfer was requested.
//
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = 0;
}
return WasDataRead;
}
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
--*/
{
PNTFS_ADVANCED_FCB_HEADER Header;
LARGE_INTEGER Offset;
LARGE_INTEGER NewFileSize;
LARGE_INTEGER OldFileSize;
#ifdef COMPRESS_ON_WIRE
PCOMPRESSION_SYNC CompressionSync = NULL;
#endif
PDEVICE_OBJECT targetVdo = IoGetRelatedDeviceObject( FileObject );
PFAST_IO_DISPATCH FastIoDispatch = targetVdo->DriverObject->FastIoDispatch;
ULONG DoingIoAtEof = FALSE;
BOOLEAN WasDataWritten = TRUE;
#ifdef SYSCACHE_DEBUG
PSCB Scb = (PSCB) FileObject->FsContext;
#endif
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
#ifdef NTFS_NO_FASTIO
UNREFERENCED_PARAMETER( FileObject );
UNREFERENCED_PARAMETER( FileOffset );
UNREFERENCED_PARAMETER( Length );
UNREFERENCED_PARAMETER( Wait );
UNREFERENCED_PARAMETER( LockKey );
UNREFERENCED_PARAMETER( Buffer );
UNREFERENCED_PARAMETER( IoStatus );
return FALSE;
#endif
//
// Don't take the fast io path if someone is already active in this thread.
//
if (IoGetTopLevelIrp() != NULL) {
return FALSE;
}
//
// Get a real pointer to the common fcb header
//
Header = (PNTFS_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 (!FlagOn( FileObject->Flags, FO_WRITE_THROUGH ) &&
CcCanIWrite( FileObject, Length, Wait, FALSE ) &&
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
//
ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
//
// 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 );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
#endif
//
// 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;
}
}
#if (DBG || defined( NTFS_FREE_ASSERTS ))
} else {
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
#endif
}
}
NtfsReleaseFsrtlHeader( (PSCB) Header );
//
// 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 there is a compressed stream and we are DoingIoAtEof, then get
// out because we could deadlock on a recursive flush from the synchronize.
//
if ((FileObject->PrivateCacheMap == NULL) ||
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
/* Remove? */ (NewFileSize.LowPart > Header->AllocationSize.QuadPart) ||
(Offset.LowPart >= (Header->ValidDataLength.LowPart + 0x2000)) ||
(NewFileSize.HighPart != 0) ||
#ifdef COMPRESS_ON_WIRE
((((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
DoingIoAtEof)
#else
FALSE
#endif
) {
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;
}
}
//
// Update both caches with EOF.
//
if (DoingIoAtEof) {
NtfsSetBothCacheSizes( FileObject,
(PCC_FILE_SIZES)&Header->AllocationSize,
(PSCB)Header );
}
//
// 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
//
IoSetTopLevelIrp( (PIRP) 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 );
#ifdef SYSCACHE_DEBUG
if (ScbIsBeingLogged( Scb )) {
FsRtlLogSyscacheEvent( Scb, SCE_ZERO_FST, 0, Header->ValidDataLength.QuadPart, Offset.QuadPart - Header->ValidDataLength.QuadPart, 0 );
}
#endif
}
#ifdef COMPRESS_ON_WIRE
//
// If there is a compressed section, update its FileSize here
//
if ((Header->FileObjectC != NULL) && DoingIoAtEof) {
CcSetFileSizes( Header->FileObjectC, (PCC_FILE_SIZES)&Header->AllocationSize );
}
#endif
//
// If there is a compressed section, then synchronize with that cache.
//
IoStatus->Status = STATUS_SUCCESS;
//
// If there is a compressed section, then we have to synchronize with
// the data out there. Note the FileObjectC better also be there, or else
// we would have made the fast I/O not possible.
//
WasDataWritten = FALSE;
#ifdef COMPRESS_ON_WIRE
if (((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
LONGLONG LocalOffset = Offset.QuadPart;
ULONG LocalLength;
ULONG LengthLeft = Length;
ASSERT( Header->FileObjectC != NULL );
do {
//
// Calculate length left in view.
//
LocalLength = LengthLeft;
if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
}
IoStatus->Status = NtfsSynchronizeUncompressedIo( (PSCB)Header,
&LocalOffset,
LocalLength,
TRUE,
&CompressionSync );
if (NT_SUCCESS(IoStatus->Status)) {
WasDataWritten = TRUE;
CcFastCopyWrite( FileObject,
(ULONG)LocalOffset,
LocalLength,
Buffer );
LocalOffset += LocalLength;
LengthLeft -= LocalLength;
Buffer = Add2Ptr( Buffer, LocalLength );
}
} while ((LengthLeft != 0) && NT_SUCCESS( IoStatus->Status ));
} else {
#endif
CcFastCopyWrite( FileObject,
Offset.LowPart,
Length,
Buffer );
WasDataWritten = TRUE;
#ifdef COMPRESS_ON_WIRE
}
#endif
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
WasDataWritten = FALSE;
}
IoSetTopLevelIrp( NULL );
#ifdef COMPRESS_ON_WIRE
if (CompressionSync != NULL) {
NtfsReleaseCompressionSync( CompressionSync );
}
#endif
//
// If we succeeded, see if we have to update FileSize or
// ValidDataLength.
//
if (WasDataWritten) {
//
// 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) {
CC_FILE_SIZES CcFileSizes;
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it). Update ValidDataLength
// and finish EOF.
//
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
NtfsAcquireFsrtlHeader( (PSCB) Header );
CcGetFileSizePointer(FileObject)->LowPart = Header->FileSize.LowPart;
Header->ValidDataLength = NewFileSize;
#ifdef SYSCACHE_DEBUG
if (ScbIsBeingLogged( Scb )) {
FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_WRITE | SCE_FLAG_FASTIO, 0, 0, NewFileSize.QuadPart );
}
#endif
CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize;
NtfsVerifySizes( Header );
NtfsFinishIoAtEof( Header );
NtfsReleaseFsrtlHeader( (PSCB) Header );
#ifdef COMPRESS_ON_WIRE
//
// Update the CompressedCache with ValidDataLength.
//
if (Header->FileObjectC != NULL) {
CcSetFileSizes( Header->FileObjectC, &CcFileSizes );
}
#endif
}
goto Done1;
}
//
// Here is the 64-bit or no-wait path.
//
} else {
//
// Prevent truncates by acquiring paging I/O
//
WasDataWritten = ExAcquireResourceSharedLite( Header->PagingIoResource, Wait );
if (!WasDataWritten) {
goto Done2;
}
//
// Now synchronize with the FsRtl Header
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
//
// 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 );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
#endif
//
// 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;
}
}
#if (DBG || defined( NTFS_FREE_ASSERTS ))
} else {
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
#endif
}
}
NtfsReleaseFsrtlHeader( (PSCB) Header );
//
// 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 there is a compressed stream and we are DoingIoAtEof, then get
// out because we could deadlock on a recursive flush from the synchronize.
//
if ((FileObject->PrivateCacheMap == NULL) ||
(Header->IsFastIoPossible == FastIoIsNotPossible) ||
/* Remove? */ (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) ||
(Offset.QuadPart >= (Header->ValidDataLength.QuadPart + 0x2000)) ||
#ifdef COMPRESS_ON_WIRE
((((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
DoingIoAtEof)
#else
FALSE
#endif
) {
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;
}
}
//
// Update both caches with EOF.
//
if (DoingIoAtEof) {
NtfsSetBothCacheSizes( FileObject,
(PCC_FILE_SIZES)&Header->AllocationSize,
(PSCB)Header );
}
//
// 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
//
IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
try {
//
// See if we have to do some zeroing
//
if ( Offset.QuadPart > Header->ValidDataLength.QuadPart ) {
#ifdef SYSCACHE_DEBUG
if (ScbIsBeingLogged( Scb )) {
FsRtlLogSyscacheEvent( Scb, SCE_ZERO_FST, SCE_FLAG_ASYNC, Header->ValidDataLength.QuadPart, Offset.QuadPart, 0 );
}
#endif
WasDataWritten = CcZeroData( FileObject,
&Header->ValidDataLength,
&Offset,
Wait );
}
if (WasDataWritten) {
//
// If there is a compressed section, update its FileSize here
//
#ifdef COMPRESS_ON_WIRE
if ((Header->FileObjectC != NULL) && DoingIoAtEof) {
CcSetFileSizes( Header->FileObjectC, (PCC_FILE_SIZES)&Header->AllocationSize );
}
#endif
//
// If there is a compressed section, then synchronize with that cache.
//
IoStatus->Status = STATUS_SUCCESS;
//
// If there is a compressed section, then we have to synchronize with
// the data out there. Note the FileObjectC better also be there, or else
// we would have made the fast I/O not possible.
//
WasDataWritten = FALSE;
#ifdef COMPRESS_ON_WIRE
if (((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
LONGLONG LocalOffset = Offset.QuadPart;
ULONG LocalLength;
ULONG LengthLeft = Length;
ASSERT(Header->FileObjectC != NULL);
do {
//
// Calculate length left in view.
//
LocalLength = LengthLeft;
if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
}
IoStatus->Status = NtfsSynchronizeUncompressedIo( (PSCB)Header,
&LocalOffset,
LocalLength,
TRUE,
&CompressionSync );
if (NT_SUCCESS(IoStatus->Status)) {
WasDataWritten = CcCopyWrite( FileObject,
(PLARGE_INTEGER)&LocalOffset,
LocalLength,
Wait,
Buffer );
LocalOffset += LocalLength;
LengthLeft -= LocalLength;
Buffer = Add2Ptr( Buffer, LocalLength );
}
} while ((LengthLeft != 0) && WasDataWritten && NT_SUCCESS(IoStatus->Status));
} else {
#endif
WasDataWritten = CcCopyWrite( FileObject,
&Offset,
Length,
Wait,
Buffer );
}
#ifdef COMPRESS_ON_WIRE
}
#endif
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
WasDataWritten = FALSE;
}
IoSetTopLevelIrp( NULL );
#ifdef COMPRESS_ON_WIRE
if (CompressionSync != NULL) {
NtfsReleaseCompressionSync( CompressionSync );
}
#endif
//
// If we succeeded, see if we have to update FileSize ValidDataLength.
//
if (WasDataWritten) {
//
// 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) {
CC_FILE_SIZES CcFileSizes;
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it). Update ValidDataLength
// and finish EOF.
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
CcGetFileSizePointer(FileObject)->QuadPart = Header->FileSize.QuadPart;
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
Header->ValidDataLength = NewFileSize;
CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize;
NtfsVerifySizes( Header );
NtfsFinishIoAtEof( Header );
NtfsReleaseFsrtlHeader( (PSCB) Header );
#ifdef COMPRESS_ON_WIRE
//
// Update the CompressedCache with ValidDataLength.
//
if (Header->FileObjectC != NULL) {
CcSetFileSizes( Header->FileObjectC, &CcFileSizes );
}
#endif
}
goto Done1;
}
}
ErrOut:
WasDataWritten = FALSE;
if (DoingIoAtEof) {
NtfsAcquireFsrtlHeader( (PSCB) Header );
#ifdef COMPRESS_ON_WIRE
if (Header->FileObjectC != NULL) {
*CcGetFileSizePointer(Header->FileObjectC) = OldFileSize;
}
#endif
Header->FileSize = OldFileSize;
NtfsFinishIoAtEof( Header );
NtfsReleaseFsrtlHeader( (PSCB) Header );
}
Done1:
ExReleaseResourceLite( Header->PagingIoResource );
Done2:
FsRtlExitFileSystem();
}
} else {
//
// We could not do the I/O now.
//
WasDataWritten = FALSE;
}
return WasDataWritten;
}
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
--*/
{
PNTFS_ADVANCED_FCB_HEADER Header;
#ifdef COMPRESS_ON_WIRE
PCOMPRESSION_SYNC CompressionSync = NULL;
#endif
BOOLEAN DoingIoAtEof = FALSE;
BOOLEAN WasDataRead = 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;
//
// Overflows should've been handled by the caller.
//
ASSERT(MAXLONGLONG - FileOffset->QuadPart >= (LONGLONG)Length);
Header = (PNTFS_ADVANCED_FCB_HEADER)FileObject->FsContext;
//
// Enter the file system
//
FsRtlEnterFileSystem();
#ifdef _WIN64
//
// The following should work for either 64 or 32 bits.
// Remove the 32 bit-only version in the #else clause
// after NT2K ships.
//
**((PULONG *)&CcFastMdlReadWait) += 1;
#else
*(PULONG)CcFastMdlReadWait += 1;
#endif
//
// Acquired shared on the common fcb header
//
if (Header->PagingIoResource == NULL) {
WasDataRead = FALSE;
goto Done2;
}
(VOID)ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
//
// 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 );
//
// Set the Flag if we are in fact beyond ValidDataLength.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
} else {
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
#endif
}
}
NtfsReleaseFsrtlHeader( (PSCB) Header );
//
// 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)) {
WasDataRead = 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;
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.
//
WasDataRead = 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
//
IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
try {
//
// If there is a compressed section, then synchronize with that cache.
//
IoStatus->Status = STATUS_SUCCESS;
//
// If there is a compressed section, then we have to synchronize with
// the data out there. Note the FileObjectC better also be there, or else
// we would have made the fast I/O not possible.
//
WasDataRead = FALSE;
#ifdef COMPRESS_ON_WIRE
if (((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
LONGLONG LocalOffset = FileOffset->QuadPart;
ULONG LengthRemaining = Length;
ULONG LocalLength;
ASSERT(Header->FileObjectC != NULL);
//
// If we are doing DoingIoAtEof then take the long path. Otherwise a recursive
// flush will try to reacquire DoingIoAtEof and deadlock.
//
if (DoingIoAtEof) {
WasDataRead = FALSE;
} else {
do {
//
// Calculate length left in view.
//
LocalLength = LengthRemaining;
if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
}
IoStatus->Status = NtfsSynchronizeUncompressedIo( (PSCB)Header,
&LocalOffset,
LocalLength,
FALSE,
&CompressionSync );
if (NT_SUCCESS(IoStatus->Status)) {
#ifdef NTFS_RWCMP_TRACE
if (NtfsCompressionTrace && IsSyscache(Header)) {
DbgPrint("CcMdlRead(F): FO = %08lx, Len = %08lx\n", (ULONG)LocalOffset, LocalLength );
}
#endif
CcMdlRead( FileObject,
(PLARGE_INTEGER)&LocalOffset,
LocalLength,
MdlChain,
IoStatus );
LocalOffset += LocalLength;
LengthRemaining -= LocalLength;
}
} while ((LengthRemaining != 0) && NT_SUCCESS(IoStatus->Status));
//
// Store final return byte count.
//
if (NT_SUCCESS( IoStatus->Status )) {
IoStatus->Information = Length;
}
}
} else {
#endif
#ifdef NTFS_RWCMP_TRACE
if (NtfsCompressionTrace && IsSyscache(Header)) {
DbgPrint("CcMdlRead(F): FO = %08lx, Len = %08lx\n", FileOffset->LowPart, Length );
}
#endif
CcMdlRead( FileObject, FileOffset, Length, MdlChain, IoStatus );
WasDataRead = TRUE;
#ifdef COMPRESS_ON_WIRE
}
#endif
FileObject->Flags |= FO_FILE_FAST_IO_READ;
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
WasDataRead = FALSE;
}
IoSetTopLevelIrp( NULL );
#ifdef COMPRESS_ON_WIRE
if (CompressionSync != NULL) {
NtfsReleaseCompressionSync( CompressionSync );
}
#endif
Done: NOTHING;
if (DoingIoAtEof) {
FsRtlUnlockFsRtlHeader( Header );
}
ExReleaseResourceLite( Header->PagingIoResource );
Done2: NOTHING;
FsRtlExitFileSystem();
}
return WasDataRead;
}
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
--*/
{
PNTFS_ADVANCED_FCB_HEADER Header;
LARGE_INTEGER Offset, NewFileSize;
LARGE_INTEGER OldFileSize;
#ifdef COMPRESS_ON_WIRE
PCOMPRESSION_SYNC CompressionSync = NULL;
#endif
ULONG DoingIoAtEof = FALSE;
BOOLEAN WasDataWritten = TRUE;
UNREFERENCED_PARAMETER( DeviceObject );
PAGED_CODE();
//
// Get a real pointer to the common fcb header
//
Header = (PNTFS_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
//
ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
//
// Now synchronize with the FsRtl Header
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
//
// 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 );
//
// Set the Flag if we are changing FileSize or ValidDataLength,
// and save current values.
//
if (DoingIoAtEof) {
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
#endif
//
// 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;
}
}
#if (DBG || defined( NTFS_FREE_ASSERTS ))
} else {
ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
#endif
}
}
NtfsReleaseFsrtlHeader( (PSCB) Header );
//
// 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;
}
}
//
// Update both caches with EOF.
//
if (DoingIoAtEof) {
NtfsSetBothCacheSizes( FileObject,
(PCC_FILE_SIZES)&Header->AllocationSize,
(PSCB)Header );
}
//
// 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
//
IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
try {
//
// See if we have to do some zeroing
//
if ( Offset.QuadPart > Header->ValidDataLength.QuadPart ) {
WasDataWritten = CcZeroData( FileObject,
&Header->ValidDataLength,
&Offset,
TRUE );
}
if (WasDataWritten) {
#ifdef COMPRESS_ON_WIRE
//
// If there is a compressed section, update its FileSize here
//
if ((Header->FileObjectC != NULL) && DoingIoAtEof) {
CcSetFileSizes( Header->FileObjectC, (PCC_FILE_SIZES)&Header->AllocationSize );
}
#endif
//
// If there is a compressed section, then synchronize with that cache.
//
IoStatus->Status = STATUS_SUCCESS;
//
// If there is a compressed section, then we have to synchronize with
// the data out there. Note the FileObjectC better also be there, or else
// we would have made the fast I/O not possible.
//
#ifdef COMPRESS_ON_WIRE
if (((PSCB)Header)->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
LONGLONG LocalOffset = Offset.QuadPart;
ULONG LocalLength;
ULONG LengthLeft = Length;
ASSERT(Header->FileObjectC != NULL);
//
// If we are doing DoingIoAtEof then take the long path. Otherwise a recursive
// flush will try to reacquire DoingIoAtEof and deadlock.
//
if (DoingIoAtEof) {
WasDataWritten = FALSE;
} else {
do {
//
// Calculate length left in view.
//
LocalLength = LengthLeft;
if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
}
IoStatus->Status = NtfsSynchronizeUncompressedIo( (PSCB)Header,
&LocalOffset,
LocalLength,
TRUE,
&CompressionSync );
if (NT_SUCCESS(IoStatus->Status)) {
#ifdef NTFS_RWCMP_TRACE
if (NtfsCompressionTrace && IsSyscache(Header)) {
DbgPrint("CcMdlWrite(F): FO = %08lx, Len = %08lx\n", (ULONG)LocalOffset, LocalLength );
}
#endif
CcPrepareMdlWrite( FileObject,
(PLARGE_INTEGER)&LocalOffset,
LocalLength,
MdlChain,
IoStatus );
LocalOffset += LocalLength;
LengthLeft -= LocalLength;
}
} while ((LengthLeft != 0) && NT_SUCCESS(IoStatus->Status));
WasDataWritten = TRUE;
}
} else {
#endif
#ifdef NTFS_RWCMP_TRACE
if (NtfsCompressionTrace && IsSyscache(Header)) {
DbgPrint("CcMdlWrite(F): FO = %08lx, Len = %08lx\n", Offset.LowPart, Length );
}
#endif
CcPrepareMdlWrite( FileObject, &Offset, Length, MdlChain, IoStatus );
WasDataWritten = TRUE;
}
#ifdef COMPRESS_ON_WIRE
}
#endif
} except( FsRtlIsNtstatusExpected(GetExceptionCode())
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH ) {
WasDataWritten = FALSE;
}
IoSetTopLevelIrp( NULL );
#ifdef COMPRESS_ON_WIRE
if (CompressionSync != NULL) {
NtfsReleaseCompressionSync( CompressionSync );
}
#endif
//
// If we succeeded, see if we have to update FileSize ValidDataLength.
//
if (WasDataWritten) {
//
// Set this handle as having modified the file
//
FileObject->Flags |= FO_FILE_MODIFIED;
IoStatus->Information = Length;
if (DoingIoAtEof) {
CC_FILE_SIZES CcFileSizes;
//
// Make sure Cc knows the current FileSize, as set above,
// (we may not have changed it). Update ValidDataLength
// and finish EOF.
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
CcGetFileSizePointer(FileObject)->QuadPart = Header->FileSize.QuadPart;
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
Header->ValidDataLength = NewFileSize;
CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize;
NtfsVerifySizes( Header );
NtfsFinishIoAtEof( Header );
NtfsReleaseFsrtlHeader( (PSCB) Header );
#ifdef COMPRESS_ON_WIRE
//
// Update the CompressedCache with ValidDataLength.
//
if (Header->FileObjectC != NULL) {
CcSetFileSizes( Header->FileObjectC, &CcFileSizes );
}
#endif
}
goto Done1;
}
ErrOut: NOTHING;
WasDataWritten = FALSE;
if (DoingIoAtEof) {
NtfsAcquireFsrtlHeader( (PSCB) Header );
#ifdef COMPRESS_ON_WIRE
if (Header->FileObjectC != NULL) {
*CcGetFileSizePointer(Header->FileObjectC) = OldFileSize;
}
#endif
Header->FileSize = OldFileSize;
NtfsFinishIoAtEof( Header );
NtfsReleaseFsrtlHeader( (PSCB) Header );
}
Done1: ExReleaseResourceLite( Header->PagingIoResource );
FsRtlExitFileSystem();
}
} else {
//
// We could not do the I/O now.
//
WasDataWritten = FALSE;
}
return WasDataWritten;
}
BOOLEAN
NtfsWaitForIoAtEof (
IN PNTFS_ADVANCED_FCB_HEADER Header,
IN OUT PLARGE_INTEGER FileOffset,
IN ULONG Length
)
/*++
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
--*/
{
EOF_WAIT_BLOCK EofWaitBlock;
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
//
NtfsReleaseFsrtlHeader( (PSCB) Header );
KeWaitForSingleObject( &EofWaitBlock.Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER)NULL);
//
// Now, resynchronize and get on with it.
//
NtfsAcquireFsrtlHeader( (PSCB) Header );
#if (DBG || defined( NTFS_FREE_ASSERTS ))
ASSERT( ((PSCB) Header)->IoAtEofThread == NULL );
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
#endif
//
// 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 PNTFS_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 (DBG || defined( NTFS_FREE_ASSERTS ))
((PSCB) Header)->IoAtEofThread = NULL;
#endif
//
// 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 );
}
}