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

1482 lines
40 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
CacheSup.c
Abstract:
This module implements the cache management routines for Ntfs
Author:
Your Name [Email] dd-Mon-Year
Revision History:
--*/
#include "NtfsProc.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (NTFS_BUG_CHECK_CACHESUP)
#define MAX_ZERO_THRESHOLD (0x00400000)
//
// Local debug trace level
//
#define Dbg (DEBUG_TRACE_CACHESUP)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsCompleteMdl)
#pragma alloc_text(PAGE, NtfsCreateInternalStreamCommon)
#pragma alloc_text(PAGE, NtfsDeleteInternalAttributeStream)
#pragma alloc_text(PAGE, NtfsMapStream)
#pragma alloc_text(PAGE, NtfsPinMappedData)
#pragma alloc_text(PAGE, NtfsPinStream)
#pragma alloc_text(PAGE, NtfsPreparePinWriteStream)
#pragma alloc_text(PAGE, NtfsZeroData)
#ifdef _CAIRO_
#pragma alloc_text(PAGE, NtOfsPutData)
#endif _CAIRO_
#endif
VOID
NtfsCreateInternalStreamCommon (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN BOOLEAN UpdateScb,
IN BOOLEAN CompressedStream
)
/*++
Routine Description:
This routine is called to prepare a stream file associated with a
particular attribute of a file. On return, the Scb for the attribute
will have an associated stream file object. On return, this
stream file will have been initialized through the cache manager.
TEMPCODE The following assumptions have been made or if open issue,
still unresolved.
- Assume. The call to create Scb will initialize the Mcb for
the non-resident case.
- Assume. When this file is created I increment the open count
but not the unclean count for this Scb. When we are done with
the stream file, we should uninitialize it and dereference it.
We also set the file object pointer to NULL. Close will then
do the correct thing.
- Assume. Since this call is likely to be followed shortly by
either a read or write, the cache map is initialized here.
Arguments:
Scb - Supplies the address to store the Scb for this attribute and
stream file. This will exist on return from this function.
UpdateScb - Indicates if the caller wants to update the Scb from the
attribute.
CompressedStream - Supplies TRUE if caller wishes to create the
compressed stream.
Return Value:
None.
--*/
{
PVCB Vcb = Scb->Vcb;
CC_FILE_SIZES CcFileSizes;
PFILE_OBJECT CallersFileObject;
PFILE_OBJECT *FileObjectPtr = &Scb->FileObject;
PFILE_OBJECT UnwindStreamFile = NULL;
BOOLEAN UnwindInitializeCacheMap = FALSE;
BOOLEAN DecrementScbCleanup = FALSE;
BOOLEAN AcquiredFastMutex = FALSE;
ASSERT_IRP_CONTEXT( IrpContext );
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsCreateInternalAttributeStream\n") );
DebugTrace( 0, Dbg, ("Scb -> %08lx\n", Scb) );
//
// Change FileObjectPtr if he wants the compressed stream
//
if (CompressedStream) {
FileObjectPtr = &Scb->Header.FileObjectC;
}
//
// If there is no file object, we create one and initialize
// it.
//
if (*FileObjectPtr == NULL) {
//
// Only acquire the mutex if we don't have the file exclusive.
//
if (!NtfsIsExclusiveScb( Scb )) {
ExAcquireFastMutexUnsafe( &StreamFileCreationFastMutex );
AcquiredFastMutex = TRUE;
}
try {
//
// Someone could have gotten there first.
//
if (*FileObjectPtr == NULL) {
UnwindStreamFile = IoCreateStreamFileObject( NULL, Scb->Vcb->Vpb->RealDevice );
//
// Propagate any flags from the caller's FileObject to our
// stream file that the Cache Manager may look at, so we do not
// miss hints like sequential only or temporary.
//
if (!FlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE) &&
(IrpContext->OriginatingIrp != NULL) &&
(CallersFileObject = IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->FileObject)) {
SetFlag( UnwindStreamFile->Flags,
CallersFileObject->Flags & NTFS_FO_PROPAGATE_TO_STREAM );
}
UnwindStreamFile->SectionObjectPointer = &Scb->NonpagedScb->SegmentObject;
//
// For a compressed stream, we have to use separate section
// object pointers.
//
if (CompressedStream) {
UnwindStreamFile->SectionObjectPointer = &Scb->NonpagedScb->SegmentObjectC;
}
//
// If we have created the stream file, we set it to type
// 'StreamFileOpen'
//
NtfsSetFileObject( UnwindStreamFile,
StreamFileOpen,
Scb,
NULL );
if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
SetFlag( UnwindStreamFile->Flags, FO_TEMPORARY_FILE );
}
//
// Initialize the fields of the file object.
//
UnwindStreamFile->ReadAccess = TRUE;
UnwindStreamFile->WriteAccess = TRUE;
UnwindStreamFile->DeleteAccess = TRUE;
//
// Increment the open count and set the section
// object pointers. We don't set the unclean count as the
// cleanup call has already occurred.
//
NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
//
// Increment the cleanup count in this Scb to prevent the
// Scb from going away if the cache call fails.
//
InterlockedIncrement( &Scb->CleanupCount );
DecrementScbCleanup = TRUE;
//
// If the Scb header has not been initialized, we will do so now.
//
if (UpdateScb
&& !FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
}
//
// We also want to set the MODIFIED_NO_WRITE flag so that
// we will tell the Cache Manager that we do not want to allow
// modified page writing, and so that we will tell the FT driver to
// serialize writes. Set this stream to MODIFIED_NO_WRITE if
//
// 1 - Any stream with with non-$DATA type code (or)
// 2 - This stream is USA protected (or)
// 3 - This is a stream of compressed data (or)
// 4 - This stream is the volume bitmap stream (or)
// 5 - A restart is in progress (or)
//
ExAcquireFastMutex( Scb->Header.FastMutex );
if ((Scb->AttributeTypeCode != $DATA) ||
FlagOn(Scb->ScbState, SCB_STATE_USA_PRESENT) ||
(Scb == Vcb->BitmapScb) ||
FlagOn( Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS )) {
SetFlag( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE );
} else if (!CompressedStream) {
SetFlag(Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE);
}
ExReleaseFastMutex( Scb->Header.FastMutex );
//
// Check if we need to initialize the cache map for the stream file.
// The size of the section to map will be the current allocation
// for the stream file.
//
if (UnwindStreamFile->PrivateCacheMap == NULL) {
BOOLEAN PinAccess;
CcFileSizes = *(PCC_FILE_SIZES)&Scb->Header.AllocationSize;
//
// If this is a stream with Usa protection, we want to tell
// the Cache Manager we do not need to get any valid data
// callbacks. We do this by having xxMax sitting in
// ValidDataLength for the call, but we have to restore the
// correct value afterwards.
//
// We also do this for all of the stream files created during
// restart. This has the effect of telling Mm to always
// fault the page in from disk. Don't generate a zero page if
// push up the file size during restart.
//
if (FlagOn( Scb->ScbState, SCB_STATE_USA_PRESENT ) ||
(Scb == Vcb->BitmapScb) ||
(Scb->AttributeTypeCode == $BITMAP) ||
FlagOn( Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS )) {
CcFileSizes.ValidDataLength.QuadPart = MAXLONGLONG;
}
PinAccess =
(BOOLEAN) (Scb->AttributeTypeCode != $DATA ||
FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE ) ||
NtfsSegmentNumber( &Scb->Fcb->FileReference ) < FIRST_USER_FILE_NUMBER);
CcInitializeCacheMap( UnwindStreamFile,
&CcFileSizes,
PinAccess,
&NtfsData.CacheManagerCallbacks,
(PCHAR)Scb + CompressedStream );
UnwindInitializeCacheMap = TRUE;
}
//
// Now call Cc to set the log handle for the file.
//
if (FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE ) &&
(Scb != Vcb->LogFileScb)) {
CcSetLogHandleForFile( UnwindStreamFile,
Vcb->LogHandle,
&LfsFlushToLsn );
}
//
// It is now safe to store the stream file in the Scb. We wait
// until now because we don't want an unsafe tester to use the
// file object until the cache is initialized.
//
*FileObjectPtr = UnwindStreamFile;
}
} finally {
DebugUnwind( NtfsCreateInternalAttributeStream );
//
// Undo our work if an error occurred.
//
if (AbnormalTermination()) {
//
// Uninitialize the cache file if we initialized it.
//
if (UnwindInitializeCacheMap) {
CcUninitializeCacheMap( UnwindStreamFile, NULL, NULL );
}
//
// Dereference the stream file if we created it.
//
if (UnwindStreamFile != NULL) {
ObDereferenceObject( UnwindStreamFile );
}
}
//
// Restore the Scb cleanup count.
//
if (DecrementScbCleanup) {
InterlockedDecrement( &Scb->CleanupCount );
}
if (AcquiredFastMutex) {
ExReleaseFastMutexUnsafe( &StreamFileCreationFastMutex );
}
DebugTrace( -1, Dbg, ("NtfsCreateInternalAttributeStream -> VOID\n") );
}
}
return;
}
BOOLEAN
NtfsDeleteInternalAttributeStream (
IN PSCB Scb,
IN BOOLEAN ForceClose
)
/*++
Routine Description:
This routine is the inverse of NtfsCreateInternalAttributeStream. It
uninitializes the cache map and dereferences the stream file object.
It is coded defensively, in case the stream file object does not exist
or the cache map has not been initialized.
Arguments:
Scb - Supplies the Scb for which the stream file is to be deleted.
ForceClose - Indicates if we to immediately close everything down or
if we are willing to let Mm slowly migrate things out.
Return Value:
BOOLEAN - TRUE if we dereference a file object, FALSE otherwise.
--*/
{
PFILE_OBJECT FileObject;
PFILE_OBJECT FileObjectC;
BOOLEAN Dereferenced = FALSE;
PAGED_CODE();
//
// We normally already have the paging Io resource. If we do
// not, then it is typically some cleanup path of create or
// whatever. This code assumes that if we cannot get the paging
// Io resource, then there is other activity still going on,
// and it is ok to not delete the stream! For example, it could
// be the lazy writer, who definitely needs the stream.
//
if (((Scb->FileObject != NULL) || (Scb->Header.FileObjectC != NULL)) &&
((Scb->Header.PagingIoResource == NULL) ||
ExAcquireResourceExclusive( Scb->Header.PagingIoResource, FALSE ))) {
ExAcquireFastMutex( &StreamFileCreationFastMutex );
//
// Capture both file objects and clear the fields so no one else
// can access them.
//
FileObject = Scb->FileObject;
Scb->FileObject = NULL;
FileObjectC = Scb->Header.FileObjectC;
Scb->Header.FileObjectC = NULL;
ExReleaseFastMutex( &StreamFileCreationFastMutex );
if (Scb->Header.PagingIoResource != NULL) {
ExReleaseResource( Scb->Header.PagingIoResource );
}
//
// Now dereference each file object.
//
if (FileObject != NULL) {
if (FileObject->PrivateCacheMap != NULL) {
CcUninitializeCacheMap( FileObject,
(ForceClose ? &Li0 : NULL),
NULL );
}
ObDereferenceObject( FileObject );
Dereferenced = TRUE;
}
if (FileObjectC != NULL) {
if (FileObjectC->PrivateCacheMap != NULL) {
CcUninitializeCacheMap( FileObjectC,
(ForceClose ? &Li0 : NULL),
NULL );
}
//
// For the compressed stream, deallocate the additional
// section object pointers.
//
ObDereferenceObject( FileObjectC );
Dereferenced = TRUE;
}
}
return Dereferenced;
}
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 );
}
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,
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
Bcb )) {
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
}
DebugTrace( -1, Dbg, ("NtfsMapStream -> 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.
--*/
{
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 (!CcPinRead( Scb->FileObject,
(PLARGE_INTEGER)&FileOffset,
Length,
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
Bcb,
Buffer )) {
ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
//
// Could not pin the data without waiting (cache miss).
//
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
}
DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) );
DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) );
DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") );
return;
}
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,
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
Bcb,
Buffer )) {
ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
//
// Could not pin the data without waiting (cache miss).
//
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
}
DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) );
DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) );
DebugTrace( -1, Dbg, ("NtfsPreparePinWriteStream -> VOID\n") );
return;
}
NTSTATUS
NtfsCompleteMdl (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine performs the function of completing Mdl read and write
requests. It should be called only from NtfsFsdRead and NtfsFsdWrite.
Arguments:
Irp - Supplies the originating Irp.
Return Value:
NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS.
--*/
{
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION IrpSp;
PAGED_CODE();
DebugTrace( +1, Dbg, ("NtfsCompleteMdl\n") );
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
//
// Do completion processing.
//
FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject;
switch( IrpContext->MajorFunction ) {
case IRP_MJ_READ:
CcMdlReadComplete( FileObject, Irp->MdlAddress );
break;
case IRP_MJ_WRITE:
ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
IrpSp = IoGetCurrentIrpStackLocation( Irp );
CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress );
break;
default:
DebugTrace( DEBUG_TRACE_ERROR, 0, ("Illegal Mdl Complete.\n") );
ASSERTMSG("Illegal Mdl Complete, About to bugcheck ", FALSE);
NtfsBugCheck( IrpContext->MajorFunction, 0, 0 );
}
//
// Mdl is now deallocated.
//
Irp->MdlAddress = NULL;
//
// Complete the request and exit right away.
//
NtfsCompleteRequest( &IrpContext, &Irp, STATUS_SUCCESS );
DebugTrace( -1, Dbg, ("NtfsCompleteMdl -> STATUS_SUCCESS\n") );
return STATUS_SUCCESS;
}
BOOLEAN
NtfsZeroData (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN PFILE_OBJECT FileObject,
IN LONGLONG StartingZero,
IN LONGLONG ByteCount
)
/*++
Routine Description:
This routine is called to zero a range of a file in order to
advance valid data length.
Arguments:
Scb - Scb for the stream to zero.
FileObject - FileObject for the stream.
StartingZero - Offset to begin the zero operation.
ByteCount - Length of range to zero.
Return Value:
BOOLEAN - TRUE if the entire range was zeroed, FALSE if the request
is broken up or the cache manager would block.
--*/
{
LONGLONG Temp;
ULONG SectorSize;
BOOLEAN Finished;
BOOLEAN CompleteZero = TRUE;
BOOLEAN ScbAcquired = FALSE;
PVCB Vcb = Scb->Vcb;
LONGLONG ZeroStart;
LONGLONG BeyondZeroEnd;
ULONG CompressionUnit = Scb->CompressionUnit;
BOOLEAN Wait;
PAGED_CODE();
Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
SectorSize = Vcb->BytesPerSector;
//
// If this is a non-compressed file and the amount to zero is larger
// than our threshold then limit the range.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_COMPRESSED ) &&
(ByteCount > MAX_ZERO_THRESHOLD)) {
ByteCount = MAX_ZERO_THRESHOLD;
CompleteZero = FALSE;
}
ZeroStart = StartingZero + (SectorSize - 1);
(ULONG)ZeroStart &= ~(SectorSize - 1);
BeyondZeroEnd = StartingZero + ByteCount + (SectorSize - 1);
(ULONG)BeyondZeroEnd &= ~(SectorSize - 1);
//
// If this is a compressed file and we are zeroing a lot, then let's
// just delete the space instead of writing tons of zeros and deleting
// the space in the noncached path!
//
if (FlagOn(Scb->ScbState, SCB_STATE_COMPRESSED) &&
(ByteCount > (Scb->CompressionUnit * 2))) {
//
// Find the end of the first compression unit being zeroed.
//
Temp = ZeroStart + (CompressionUnit - 1);
(ULONG)Temp &= ~(CompressionUnit - 1);
//
// Zero the first compression unit.
//
if ((ULONG)Temp != (ULONG)ZeroStart) {
Finished = CcZeroData( FileObject, (PLARGE_INTEGER)&ZeroStart, (PLARGE_INTEGER)&Temp, Wait );
if (!Finished) {return FALSE;}
ZeroStart = Temp;
}
//
// Calculate the start of the last compression unit.
//
Temp = BeyondZeroEnd;
(ULONG)Temp &= ~(CompressionUnit - 1);
//
// Zero the beginning of the last compression unit.
//
if ((ULONG)Temp != (ULONG)BeyondZeroEnd) {
Finished = CcZeroData( FileObject, (PLARGE_INTEGER)&Temp, (PLARGE_INTEGER)&BeyondZeroEnd, Wait );
if (!Finished) {return FALSE;}
BeyondZeroEnd = Temp;
}
//
// Now delete all of the compression units in between.
//
Temp = LlClustersFromBytes( Vcb, BeyondZeroEnd ) - 1;
//
// If the caller has not already started a transaction (like write.c),
// then let's just do the delete as an atomic action.
//
if (!ExIsResourceAcquiredExclusive( Scb->Header.Resource )) {
NtfsAcquireExclusiveScb( IrpContext, Scb );
ScbAcquired = TRUE;
}
try {
//
// Delete the space.
//
NtfsDeleteAllocation( IrpContext,
FileObject,
Scb,
LlClustersFromBytes(Vcb, ZeroStart),
Temp,
TRUE,
TRUE );
//
// If we didn't raise then update the Scb values.
//
Scb->ValidDataToDisk = BeyondZeroEnd;
//
// If we succeed, commit the atomic action. Release all of the exclusive
// resources if our user explicitly acquired the Fcb here.
//
if (ScbAcquired) {
NtfsCheckpointCurrentTransaction( IrpContext );
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
NtfsReleaseFcb( IrpContext,
(PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
FCB,
ExclusiveFcbLinks ));
}
ScbAcquired = FALSE;
}
if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
}
} finally {
if (ScbAcquired) {
NtfsReleaseScb( IrpContext, Scb );
}
}
return TRUE;
}
//
// If we were called to just zero part of a sector we are screwed.
//
if (ZeroStart == BeyondZeroEnd) {
return TRUE;
}
Finished = CcZeroData( FileObject,
(PLARGE_INTEGER)&ZeroStart,
(PLARGE_INTEGER)&BeyondZeroEnd,
Wait );
//
// If we are breaking this request up then commit the current
// transaction (including updating the valid data length in
// in the Scb) and return FALSE.
//
if (Finished && !CompleteZero) {
//
// Synchronize the valid data length change using the mutex.
//
ExAcquireFastMutex( Scb->Header.FastMutex );
Scb->Header.ValidDataLength.QuadPart = BeyondZeroEnd;
ExReleaseFastMutex( Scb->Header.FastMutex );
NtfsCheckpointCurrentTransaction( IrpContext );
return FALSE;
}
return Finished;
}
#ifdef _CAIRO_
NTFSAPI
VOID
NtOfsPutData (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb,
IN LONGLONG Offset,
IN ULONG Length,
IN PVOID Data OPTIONAL
)
/*++
Routine Description:
This routine is called to update a range of a recoverable stream.
Arguments:
Scb - Scb for the stream to zero.
Offset - Offset in stream to update.
Length - Length of stream to update in bytes.
Data - Data to update stream with if specified, else range should be zeroed.
Return Value:
None.
--*/
{
PAGED_CODE();
ASSERT((Offset + Length) <= Scb->Header.FileSize.QuadPart);
ASSERT(FlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE));
//
// First handle the resident case.
//
if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
ATTRIBUTE_ENUMERATION_CONTEXT Context;
PFILE_RECORD_SEGMENT_HEADER FileRecord;
PATTRIBUTE_RECORD_HEADER Attribute;
ULONG RecordOffset, AttributeOffset;
PVCB Vcb = Scb->Vcb;
NtfsInitializeAttributeContext( &Context );
try {
//
// Lookup and pin the attribute.
//
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
NtfsPinMappedAttribute( IrpContext, Vcb, &Context );
//
// Extract the relevant pointers and calculate offsets.
//
FileRecord = NtfsContainingFileRecord(&Context);
Attribute = NtfsFoundAttribute(&Context);
RecordOffset = PtrOffset(FileRecord, Attribute);
AttributeOffset = Attribute->Form.Resident.ValueOffset + (ULONG)Offset;
//
// Log the change while we still have the old data.
//
FileRecord->Lsn =
NtfsWriteLog( IrpContext,
Vcb->MftScb,
NtfsFoundBcb(&Context),
UpdateResidentValue,
Data,
Length,
UpdateResidentValue,
Add2Ptr(Attribute, Attribute->Form.Resident.ValueOffset + (ULONG)Offset),
Length,
NtfsMftOffset(&Context),
RecordOffset,
AttributeOffset,
Vcb->BytesPerFileRecordSegment );
//
// Now update this data by calling the same routine as restart.
//
NtfsRestartChangeValue( IrpContext,
FileRecord,
RecordOffset,
AttributeOffset,
Data,
Length,
FALSE );
//
// If there is a stream for this attribute, then we must update it in the
// cache, copying from the attribute itself in order to handle the zeroing
// (Data == NULL) case.
//
if (Scb->FileObject != NULL) {
CcCopyWrite( Scb->FileObject,
(PLARGE_INTEGER)&Offset,
Length,
TRUE,
Add2Ptr(Attribute, AttributeOffset) );
}
//
// Optionally update ValidDataLength
//
Offset += Length;
if (Offset > Scb->Header.ValidDataLength.QuadPart) {
Scb->Header.ValidDataLength.QuadPart = Offset;
}
} finally {
NtfsCleanupAttributeContext( &Context );
}
//
// Now handle the nonresident case.
//
} else {
PVOID Buffer;
LONGLONG NewValidDataLength = Offset + Length;
PBCB Bcb = NULL;
ULONG PageOffset = (ULONG)Offset & (PAGE_SIZE - 1);
ULONG MovingBackwards = FALSE;
ASSERT(Scb->FileObject != NULL);
ASSERT((Offset & ~(VACB_MAPPING_GRANULARITY - 1)) == ((Offset + Length - 1) & ~(VACB_MAPPING_GRANULARITY - 1)));
//
// If we are starting beyond ValidDataLength, then recurse to
// zero what we need.
//
if (Offset > Scb->Header.ValidDataLength.QuadPart) {
ASSERT((Offset - Scb->Header.ValidDataLength.QuadPart) <= MAXULONG);
NtOfsPutData( IrpContext,
Scb,
Scb->Header.ValidDataLength.QuadPart,
(ULONG)(Offset - Scb->Header.ValidDataLength.QuadPart),
NULL );
}
try {
//
// Now loop until there are no more pages with new data
// to log.
//
while (Length != 0) {
ULONG BytesThisPage;
NtfsPinStream( IrpContext,
Scb,
Offset,
1,
&Bcb,
&Buffer );
//
// Compute the number of bytes of for this page, assuming a
// forward move.
//
BytesThisPage = PAGE_SIZE - PageOffset;
if (BytesThisPage > Length) {
BytesThisPage = Length;
}
//
// See if we need to switch to moving backwards.
//
if (!MovingBackwards &&
((PCHAR)Buffer > (PCHAR)Data) &&
(Data != NULL) &&
((PageOffset + Length) > PAGE_SIZE)) {
//
// We are now doing the move backwards - we will only do this once.
//
MovingBackwards = TRUE;
//
// Figure out how many bytes there are to move in the last page, and
// then see how much we have to adjust our Offset and pointers by to
// get to the last page (temporarily in PageOffset).
//
BytesThisPage = ((PageOffset + Length - 1) & (PAGE_SIZE - 1)) + 1;
PageOffset = Length - BytesThisPage;
//
// Now adjust everyone by the right amount.
//
Offset += PageOffset;
Data = Add2Ptr( Data, PageOffset );
Buffer = Add2Ptr( Buffer, PageOffset );
//
// Of course the page offset in the last page is 0.
//
PageOffset = 0;
}
//
// Now log the changes to this page.
//
(VOID)
NtfsWriteLog( IrpContext,
Scb,
Bcb,
UpdateNonresidentValue,
Data,
BytesThisPage,
UpdateNonresidentValue,
Buffer,
BytesThisPage,
Offset - PageOffset,
PageOffset,
0,
PageOffset + BytesThisPage );
//
// Move the data into place.
//
if (Data != NULL) {
RtlMoveMemory( Buffer, Data, BytesThisPage );
} else {
RtlZeroMemory( Buffer, BytesThisPage );
}
//
// Now we pin the page and calculate the beginning
// buffer in the page.
//
NtfsUnpinBcb( &Bcb );
Length -= BytesThisPage;
PageOffset = 0;
if (MovingBackwards) {
//
// Now decrement the counts and move through the
// caller's buffer.
//
BytesThisPage = PAGE_SIZE;
if (Length < PAGE_SIZE) {
PageOffset = PAGE_SIZE - Length;
BytesThisPage = Length;
}
Data = Add2Ptr( Data, (0 - BytesThisPage) );
Offset -= BytesThisPage;
} else {
//
// Now decrement the counts and move through the
// caller's buffer.
//
if (Data != NULL) {
Data = Add2Ptr( Data, BytesThisPage );
}
Offset += BytesThisPage;
}
}
//
// Optionally update ValidDataLength
//
if (NewValidDataLength > Scb->Header.ValidDataLength.QuadPart) {
Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
NtfsWriteFileSizes( IrpContext, Scb, &Offset, TRUE, TRUE );
}
} finally {
NtfsUnpinBcb( &Bcb );
}
}
}
#endif _CAIRO_