VOID NtfsReleaseScbFromLazyWrite ( IN PVOID OpaqueScb ) /*++ Routine Description: The address of this routine is specified when creating a CacheMap for a file. It is subsequently called by the Lazy Writer after its performing lazy writes to the file. Arguments: Scb - The Scb which was specified as a context parameter for this routine. Return Value: None --*/ { ULONG CompressedStream = (ULONG)OpaqueScb & 1; PSCB Scb = (PSCB)((ULONG)OpaqueScb & ~1); PFCB Fcb = Scb->Fcb; ASSERT_SCB(Scb); PAGED_CODE(); // // Clear the toplevel at this point, if we set it above. // if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP) { IoSetTopLevelIrp( NULL ); } Scb->LazyWriteThread[CompressedStream] = NULL; if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) { NOTHING; } else if (Scb->Header.PagingIoResource != NULL) { ExReleaseResource( Scb->Header.PagingIoResource ); } else { ExReleaseResource( Scb->Header.Resource ); } return; }
VOID NtfsReleaseScbWithPaging ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb ) /*++ Routine Description: This routine releases access to the Scb, including its paging I/O resource if it exists. Arguments: Scb - Supplies the Fcb to acquire Return Value: None. --*/ { PFCB Fcb = Scb->Fcb; ASSERT_IRP_CONTEXT(IrpContext); ASSERT_SCB(Scb); PAGED_CODE(); // // Release the paging Io resource in the Scb under the following // conditions. // // - No transaction underway // - This paging Io resource is in the IrpContext // (This last test insures there is a paging IO resource // and we own it). // if ((IrpContext->TransactionId == 0) && (IrpContext->FcbWithPagingExclusive == Fcb)) { NtfsReleasePagingIo( IrpContext, Fcb ); } NtfsReleaseScb( IrpContext, Scb ); }
VOID NtfsSetFileObject ( IN PFILE_OBJECT FileObject, IN TYPE_OF_OPEN TypeOfOpen, IN PSCB Scb, IN PCCB Ccb OPTIONAL ) /*++ Routine Description: This routine sets the file system pointers within the file object Arguments: FileObject - Supplies a pointer to the file object being modified. TypeOfOpen - Supplies the type of open denoted by the file object. This is only used by this procedure for sanity checking. Scb - Supplies a pointer to Scb for the file object. Ccb - Optionally supplies a pointer to a ccb Return Value: None. --*/ { ASSERT_FILE_OBJECT( FileObject ); ASSERT_SCB( Scb ); ASSERT_OPTIONAL_CCB( Ccb ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsSetFileObject, FileObject = %08lx\n", FileObject) ); // // Load up the FileObject fields. // FileObject->FsContext = Scb; FileObject->FsContext2 = Ccb; FileObject->Vpb = Scb->Vcb->Vpb; // // Now store TypeOfOpen if there is a Ccb // ASSERT((Ccb != NULL) || (TypeOfOpen == StreamFileOpen) || (TypeOfOpen == UnopenedFileObject)); if (Ccb != NULL) { Ccb->TypeOfOpen = (UCHAR)TypeOfOpen; } // // If this file has the temporary attribute bit set, don't lazy // write it unless absolutely necessary. // if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) { SetFlag( FileObject->Flags, FO_TEMPORARY_FILE ); } // // And return to our caller // DebugTrace( -1, Dbg, ("NtfsSetFileObject -> VOID\n") ); return; }
VOID NtfsReleaseScbFromReadAhead ( IN PVOID OpaqueScb ) /*++ Routine Description: The address of this routine is specified when creating a CacheMap for a file. It is subsequently called by the Lazy Writer after its read ahead. Arguments: Scb - The Scb which was specified as a context parameter for this routine. Return Value: None --*/ { PREAD_AHEAD_THREAD ReadAheadThread; PVOID CurrentThread; KIRQL OldIrql; PSCB Scb = (PSCB)OpaqueScb; PFCB Fcb = Scb->Fcb; ASSERT_SCB(Scb); // // Free our read ahead entry. // KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &OldIrql ); CurrentThread = (PVOID)PsGetCurrentThread(); ReadAheadThread = (PREAD_AHEAD_THREAD)NtfsData.ReadAheadThreads.Flink; while ((ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) && (ReadAheadThread->Thread != CurrentThread)) { ReadAheadThread = (PREAD_AHEAD_THREAD)ReadAheadThread->Links.Flink; } ASSERT(ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads); ReadAheadThread->Thread = NULL; // // Move him to the end of the list so all the allocated entries are at // the front, and we simplify our scans. // RemoveEntryList( &ReadAheadThread->Links ); InsertTailList( &NtfsData.ReadAheadThreads, &ReadAheadThread->Links ); KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, OldIrql ); if (Scb->Header.PagingIoResource != NULL) { ExReleaseResource( Scb->Header.PagingIoResource ); } return; }
BOOLEAN NtfsAcquireScbForReadAhead ( IN PVOID OpaqueScb, IN BOOLEAN Wait ) /*++ Routine Description: The address of this routine is specified when creating a CacheMap for a file. It is subsequently called by the Lazy Writer prior to its performing read ahead to the file. Arguments: Scb - The Scb which was specified as a context parameter for this routine. Wait - TRUE if the caller is willing to block. Return Value: FALSE - if Wait was specified as FALSE and blocking would have been required. The Fcb is not acquired. TRUE - if the Scb has been acquired --*/ { PREAD_AHEAD_THREAD ReadAheadThread; PVOID CurrentThread; KIRQL OldIrql; PSCB Scb = (PSCB)OpaqueScb; PFCB Fcb = Scb->Fcb; BOOLEAN AcquiredFile = FALSE; ASSERT_SCB(Scb); // // Acquire the Scb only for those files that the read wil // acquire it for, i.e., not the first set of system files. // Otherwise we can deadlock, for example with someone needing // a new Mft record. // if ((Scb->Header.PagingIoResource == NULL) || ExAcquireResourceShared( Scb->Header.PagingIoResource, Wait )) { AcquiredFile = TRUE; // // Add our thread to the read ahead list. // KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &OldIrql ); CurrentThread = (PVOID)PsGetCurrentThread(); ReadAheadThread = (PREAD_AHEAD_THREAD)NtfsData.ReadAheadThreads.Flink; while ((ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) && (ReadAheadThread->Thread != NULL)) { // // We better not already see ourselves. // ASSERT( ReadAheadThread->Thread != CurrentThread ); ReadAheadThread = (PREAD_AHEAD_THREAD)ReadAheadThread->Links.Flink; } // // If we hit the end of the list, then allocate a new one. Note we // should have at most one entry per critical worker thread in the // system. // if (ReadAheadThread == (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) { ReadAheadThread = ExAllocatePoolWithTag( NonPagedPool, sizeof(READ_AHEAD_THREAD), 'RftN' ); // // If we failed to allocate an entry, clean up and raise. // if (ReadAheadThread == NULL) { KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, OldIrql ); if (NtfsSegmentNumber( &Fcb->FileReference ) > VOLUME_DASD_NUMBER) { if (Scb->Header.PagingIoResource != NULL) { ExReleaseResource( Scb->Header.PagingIoResource ); } } ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } InsertTailList( &NtfsData.ReadAheadThreads, &ReadAheadThread->Links ); } ReadAheadThread->Thread = CurrentThread; KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, OldIrql ); } return AcquiredFile; }
NTSTATUS NtfsAcquireFileForModWrite ( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER EndingOffset, OUT PERESOURCE *ResourceToRelease, IN PDEVICE_OBJECT DeviceObject ) { BOOLEAN AcquiredFile; PSCB Scb = (PSCB) (FileObject->FsContext); PFCB Fcb = Scb->Fcb; ASSERT_SCB(Scb); UNREFERENCED_PARAMETER( DeviceObject ); PAGED_CODE(); // // Acquire the Scb only for those files that the write will // acquire it for, i.e., not the first set of system files. // Otherwise we can deadlock, for example with someone needing // a new Mft record. // if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) { // // We need to synchronize the lazy writer with the clean volume // checkpoint. We do this by acquiring and immediately releasing this // Scb. This is to prevent the lazy writer from flushing the log file // when the space may be at a premium. // if (AcquiredFile = ExAcquireResourceShared( Scb->Header.Resource, FALSE )) { ExReleaseResource( Scb->Header.Resource ); } *ResourceToRelease = NULL; // // Now acquire either the main or paging io resource depending on the // state of the file. // } else { // // Figure out which resource to acquire. // if (Scb->Header.PagingIoResource != NULL) { *ResourceToRelease = Scb->Header.PagingIoResource; } else { *ResourceToRelease = Scb->Header.Resource; } // // Try to acquire the resource with Wait FALSE // AcquiredFile = ExAcquireResourceShared( *ResourceToRelease, FALSE ); // // If we got the resource, check if he is possibly trying to extend // ValidDataLength. If so that will cause us to go into useless mode // possibly doing actual I/O writing zeros out to the file past actual // valid data in the cache. This is so inefficient that it is better // to tell MM not to do this write. // if (AcquiredFile) { if (Scb->CompressionUnit != 0) { ExAcquireFastMutex( Scb->Header.FastMutex ); if ((EndingOffset->QuadPart > Scb->ValidDataToDisk) && (EndingOffset->QuadPart < (Scb->Header.FileSize.QuadPart + PAGE_SIZE - 1)) && !FlagOn(Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) { ExReleaseResource(*ResourceToRelease); AcquiredFile = FALSE; *ResourceToRelease = NULL; } ExReleaseFastMutex( Scb->Header.FastMutex ); } } else { *ResourceToRelease = NULL; } } return (AcquiredFile ? STATUS_SUCCESS : STATUS_CANT_WAIT); }
BOOLEAN NtfsAcquireScbForLazyWrite ( IN PVOID OpaqueScb, IN BOOLEAN Wait ) /*++ Routine Description: The address of this routine is specified when creating a CacheMap for a file. It is subsequently called by the Lazy Writer prior to its performing lazy writes to the file. This callback is necessary to avoid deadlocks with the Lazy Writer. (Note that normal writes acquire the Fcb, and then call the Cache Manager, who must acquire some of his internal structures. If the Lazy Writer could not call this routine first, and were to issue a write after locking Caching data structures, then a deadlock could occur.) Arguments: OpaqueScb - The Scb which was specified as a context parameter for this routine. Wait - TRUE if the caller is willing to block. Return Value: FALSE - if Wait was specified as FALSE and blocking would have been required. The Fcb is not acquired. TRUE - if the Scb has been acquired --*/ { BOOLEAN AcquiredFile = TRUE; ULONG CompressedStream = (ULONG)OpaqueScb & 1; PSCB Scb = (PSCB)((ULONG)OpaqueScb & ~1); PFCB Fcb = Scb->Fcb; ASSERT_SCB(Scb); PAGED_CODE(); // // Acquire the Scb only for those files that the write will // acquire it for, i.e., not the first set of system files. // Otherwise we can deadlock, for example with someone needing // a new Mft record. // if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) { // // We need to synchronize the lazy writer with the clean volume // checkpoint. We do this by acquiring and immediately releasing this // Scb. This is to prevent the lazy writer from flushing the log file // when the space may be at a premium. // if (ExAcquireResourceShared( Scb->Header.Resource, Wait )) { ExReleaseResource( Scb->Header.Resource ); AcquiredFile = TRUE; } // // Now acquire either the main or paging io resource depending on the // state of the file. // } else if (Scb->Header.PagingIoResource != NULL) { AcquiredFile = ExAcquireResourceShared( Scb->Header.PagingIoResource, Wait ); } else { AcquiredFile = ExAcquireResourceShared( Scb->Header.Resource, Wait ); } if (AcquiredFile) { // // We assume the Lazy Writer only acquires this Scb once. When he // has acquired it, then he has eliminated anyone who would extend // valid data, since they must take out the resource exclusive. // Therefore, it should be guaranteed that this flag is currently // clear (the ASSERT), and then we will set this flag, to insure // that the Lazy Writer will never try to advance Valid Data, and // also not deadlock by trying to get the Fcb exclusive. // ASSERT( Scb->LazyWriteThread[CompressedStream] == NULL ); Scb->LazyWriteThread[CompressedStream] = PsGetCurrentThread(); // // Make Cc top level, so that we will not post or retry on errors. // (If it is not NULL, it must be one of our internal calls to this // routine, such as from Restart or Hot Fix.) // if (IoGetTopLevelIrp() == NULL) { IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); } } return AcquiredFile; }
VOID NtfsPreparePinWriteStream ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN LONGLONG FileOffset, IN ULONG Length, IN BOOLEAN Zero, OUT PVOID *Bcb, OUT PVOID *Buffer ) /*++ Routine Description: Arguments: Return Value: --*/ { ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_SCB( Scb ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsPreparePinWriteStream\n") ); DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) ); DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) ); // // The file object should already exist in the Scb. // ASSERT( Scb->FileObject != NULL ); // // If we are trying to go beyond the end of the allocation, assume // we have some corruption. // if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) { NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb ); } // // Call the cache manager to do it. This call may raise, or // will return FALSE if waiting is required. // if (!CcPreparePinWrite( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, Length, Zero, FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ), Bcb, Buffer )) { ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )); // // Could not pin the data without waiting (cache miss). // NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL ); } #ifdef MAPCOUNT_DBG IrpContext->MapCount++; #endif DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) ); DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) ); DebugTrace( -1, Dbg, ("NtfsPreparePinWriteStream -> VOID\n") ); return; }
VOID NtfsPinStream ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN LONGLONG FileOffset, IN ULONG Length, OUT PVOID *Bcb, OUT PVOID *Buffer ) /*++ Routine Description: This routine is called to pin a range of bytes within the stream file for an Scb. The allowed range to pin is bounded by the allocation size for the Scb. This operation is only valid on a non-resident Scb. TEMPCODE - The following need to be resolved for this routine. - Can the caller specify either an empty range or an invalid range. In that case we need to able to return the actual length of the pinned range. Arguments: Scb - This is the Scb for the operation. FileOffset - This is the offset within the Scb where the data is to be pinned. Length - This is the number of bytes to pin. Bcb - Returns a pointer to the Bcb for this range of bytes. Buffer - Returns a pointer to the range of bytes pinned in memory. Return Value: None. --*/ { NTSTATUS OldStatus = IrpContext->ExceptionStatus; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_SCB( Scb ); ASSERT( Length != 0 ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsPinStream\n") ); DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) ); DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) ); // // The file object should already exist in the Scb. // ASSERT( Scb->FileObject != NULL ); // // If we are trying to go beyond the end of the allocation, assume // we have some corruption. // if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) { NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb ); } // // Call the cache manager to map the data. This call may raise, or // will return FALSE if waiting is required. // if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) { FileOffset -= Scb->Vcb->UsnCacheBias; } if (!CcPinRead( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, Length, FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ), Bcb, Buffer )) { ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )); // // Could not pin the data without waiting (cache miss). // NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL ); } // // We don't want to propagate wether or not we hit eof. Its assumed the code pinning is // already filesize synchronized // if (IrpContext->ExceptionStatus == STATUS_END_OF_FILE) { IrpContext->ExceptionStatus = OldStatus; } #ifdef MAPCOUNT_DBG IrpContext->MapCount++; #endif DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) ); DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) ); DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") ); return; }
VOID NtfsPinMappedData ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN LONGLONG FileOffset, IN ULONG Length, IN OUT PVOID *Bcb ) /*++ Routine Description: This routine is called to pin a previously mapped range of bytes within the stream file for an Scb, for the purpose of subsequently modifying this byte range. The allowed range to map is bounded by the allocation size for the Scb. This operation is only valid on a non-resident Scb. The data is guaranteed to stay at the same virtual address as previously returned from NtfsMapStream. TEMPCODE - The following need to be resolved for this routine. - Can the caller specify either an empty range or an invalid range. In that case we need to able to return the actual length of the mapped range. Arguments: Scb - This is the Scb for the operation. FileOffset - This is the offset within the Scb where the data is to be pinned. Length - This is the number of bytes to pin. Bcb - Returns a pointer to the Bcb for this range of bytes. Return Value: None. --*/ { ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_SCB( Scb ); ASSERT( Length != 0 ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsPinMappedData\n") ); DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) ); DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) ); // // The file object should already exist in the Scb. // ASSERT( Scb->FileObject != NULL ); // // If we are trying to go beyond the end of the allocation, assume // we have some corruption. // if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) { NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb ); } // // Call the cache manager to map the data. This call may raise, but // will never return an error (including CANT_WAIT). // if (!CcPinMappedData( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, Length, FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ), Bcb )) { NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL ); } DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") ); return; }
VOID NtfsMapStream ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN LONGLONG FileOffset, IN ULONG Length, OUT PVOID *Bcb, OUT PVOID *Buffer ) /*++ Routine Description: This routine is called to map a range of bytes within the stream file for an Scb. The allowed range to map is bounded by the allocation size for the Scb. This operation is only valid on a non-resident Scb. TEMPCODE - The following need to be resolved for this routine. - Can the caller specify either an empty range or an invalid range. In that case we need to able to return the actual length of the mapped range. Arguments: Scb - This is the Scb for the operation. FileOffset - This is the offset within the Scb where the data is to be pinned. Length - This is the number of bytes to pin. Bcb - Returns a pointer to the Bcb for this range of bytes. Buffer - Returns a pointer to the range of bytes. We can fault them in by touching them, but they aren't guaranteed to stay unless we pin them via the Bcb. Return Value: None. --*/ { ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_SCB( Scb ); ASSERT( Length != 0 ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsMapStream\n") ); DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) ); DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) ); // // The file object should already exist in the Scb. // ASSERT( Scb->FileObject != NULL ); // // If we are trying to go beyond the end of the allocation, assume // we have some corruption. // if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) { NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb ); } // // Call the cache manager to map the data. This call may raise, but // will never return an error (including CANT_WAIT). // if (!CcMapData( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, Length, TRUE, Bcb, Buffer )) { NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL ); } #ifdef MAPCOUNT_DBG IrpContext->MapCount++; #endif DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) ); DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") ); return; }