/*++ 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 #ifdef SYSCACHE_DEBUG if (ScbIsBeingLogged( (PSCB)Header )) { ULONG Flags = SCE_FLAG_READ | SCE_FLAG_FASTIO; if (!Wait) { Flags |= SCE_FLAG_ASYNC; } ASSERT( ((PSCB)Header)->NonpagedScb->SegmentObject.ImageSectionObject == NULL ); FsRtlLogSyscacheEvent( ((PSCB)Header), SCE_READ, Flags, FileOffset->QuadPart, Length, (LONGLONG)FileObject ); } #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; BOOLEAN DoingIoAtEof = FALSE; BOOLEAN WasDataWritten = FALSE; #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 to do any zeroing. This is handled in the main path // which deals with sparse files - cc rollbacks etc. // // 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)) || (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; } } // // 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 { ASSERT( Offset.LowPart <= Header->ValidDataLength.LowPart ); #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 = 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) { #ifdef COMPRESS_ON_WIRE CC_FILE_SIZES CcFileSizes; #endif // // 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( ((PSCB)Header) )) { FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_WRITE | SCE_FLAG_FASTIO, 0, 0, NewFileSize.QuadPart ); } #endif #ifdef COMPRESS_ON_WIRE CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize; #endif 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 need to zero anything - handle in the main path // // 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) || #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; } } // // 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 { ASSERT( Offset.QuadPart <= Header->ValidDataLength.QuadPart ); #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) { #ifdef COMPRESS_ON_WIRE CC_FILE_SIZES CcFileSizes; #endif // // 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; #ifdef COMPRESS_ON_WIRE CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize; #endif NtfsVerifySizes( Header ); NtfsFinishIoAtEof( Header ); NtfsReleaseFsrtlHeader( (PSCB) Header ); #ifdef SYSCACHE_DEBUG if (ScbIsBeingLogged( Scb )) { FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_WRITE | SCE_FLAG_FASTIO, 0, 0, NewFileSize.QuadPart ); } #endif #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 { // // Noop case // WasDataWritten = TRUE; } } 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 BOOLEAN DoingIoAtEof = FALSE; BOOLEAN WasDataWritten = FALSE; 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 need to do any zeroing and do that in the main write path // if ((FileObject->PrivateCacheMap == NULL) || (Header->IsFastIoPossible == FastIoIsNotPossible) || /* Remove? */ (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) || (Offset.QuadPart > Header->ValidDataLength.QuadPart)) { 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 // IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP ); try { ASSERT( Offset.QuadPart <= Header->ValidDataLength.QuadPart ); #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) { #ifdef COMPRESS_ON_WIRE CC_FILE_SIZES CcFileSizes; #endif // // 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; #ifdef COMPRESS_ON_WIRE CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize; #endif NtfsVerifySizes( Header ); NtfsFinishIoAtEof( Header ); NtfsReleaseFsrtlHeader( (PSCB) Header ); #ifdef SYSCACHE_DEBUG if (ScbIsBeingLogged( (PSCB)Header )) { FsRtlLogSyscacheEvent( (PSCB)Header, SCE_VDL_CHANGE, SCE_FLAG_WRITE | SCE_FLAG_FASTIO | SCE_FLAG_MDL, 0, 0, NewFileSize.QuadPart ); } #endif #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 ); } }