1394 lines
40 KiB
C
1394 lines
40 KiB
C
/*++
|
||
|
||
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)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NtfsCreateInternalStreamCommon (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb,
|
||
IN BOOLEAN UpdateScb,
|
||
IN BOOLEAN CompressedStream,
|
||
IN UNICODE_STRING const *StreamName
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
StreamName - Internal stream name or NULL is there isn't one available.
|
||
This is a constant value so we don't have to allocate any pool.
|
||
|
||
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 AcquiredMutex = 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
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (CompressedStream) {
|
||
FileObjectPtr = &Scb->Header.FileObjectC;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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 )) {
|
||
|
||
KeWaitForSingleObject( &StreamFileCreationMutex, Executive, KernelMode, FALSE, NULL );
|
||
AcquiredMutex = TRUE;
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Someone could have gotten there first.
|
||
//
|
||
|
||
if (*FileObjectPtr == NULL) {
|
||
|
||
UnwindStreamFile = IoCreateStreamFileObjectLite( NULL, Scb->Vcb->Vpb->RealDevice);
|
||
|
||
if (ARGUMENT_PRESENT( StreamName )) {
|
||
UnwindStreamFile->FileName.MaximumLength = StreamName->MaximumLength;
|
||
UnwindStreamFile->FileName.Length = StreamName->Length;
|
||
UnwindStreamFile->FileName.Buffer = StreamName->Buffer;
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
if (CompressedStream) {
|
||
UnwindStreamFile->SectionObjectPointer = &Scb->NonpagedScb->SegmentObjectC;
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// 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 );
|
||
}
|
||
|
||
//
|
||
// If this is a compressed stream and the file is not already
|
||
// marked as MODIFIED_NO_WRITE then do it now. Use the
|
||
// Extended flag field in the Fsrtl header for this. Since this
|
||
// is the only place we make this call with FsContext2 == NULL,
|
||
// it does not matter how we leave the FsRtl header flag.!
|
||
//
|
||
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
ClearFlag(Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE);
|
||
if (!FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE ) &&
|
||
!FlagOn( Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE ) &&
|
||
!CompressedStream) {
|
||
|
||
SetFlag(Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE);
|
||
}
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
|
||
//
|
||
// 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_MODIFIED_NO_WRITE )) {
|
||
|
||
CcFileSizes.ValidDataLength.QuadPart = MAXLONGLONG;
|
||
}
|
||
|
||
PinAccess =
|
||
(BOOLEAN) (Scb->AttributeTypeCode != $DATA ||
|
||
FlagOn(Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE) ||
|
||
FlagOn( Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) ||
|
||
CompressedStream);
|
||
|
||
//
|
||
// Bias this for the Usn journal.
|
||
//
|
||
|
||
if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
|
||
|
||
CcFileSizes.AllocationSize.QuadPart -= Vcb->UsnCacheBias;
|
||
CcFileSizes.FileSize.QuadPart -= Vcb->UsnCacheBias;
|
||
}
|
||
|
||
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) {
|
||
|
||
//
|
||
// Clear the internal file name constant
|
||
//
|
||
|
||
NtfsClearInternalFilename( UnwindStreamFile );
|
||
|
||
ObDereferenceObject( UnwindStreamFile );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Restore the Scb cleanup count.
|
||
//
|
||
|
||
if (DecrementScbCleanup) {
|
||
|
||
InterlockedDecrement( &Scb->CleanupCount );
|
||
}
|
||
|
||
if (AcquiredMutex) {
|
||
|
||
KeReleaseMutant( &StreamFileCreationMutex, IO_NO_INCREMENT, FALSE, FALSE );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCreateInternalAttributeStream -> VOID\n") );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsDeleteInternalAttributeStream (
|
||
IN PSCB Scb,
|
||
IN ULONG ForceClose,
|
||
IN ULONG CompressedStreamOnly
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
CompressedStreamOnly - Indicates if we only want to delete the compressed
|
||
stream.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if we dereference a file object, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFILE_OBJECT FileObject;
|
||
#ifdef COMPRESS_ON_WIRE
|
||
PFILE_OBJECT FileObjectC;
|
||
#endif
|
||
|
||
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 (
|
||
#ifdef COMPRESS_ON_WIRE
|
||
((Scb->FileObject != NULL) || (Scb->Header.FileObjectC != NULL)) &&
|
||
#else
|
||
(Scb->FileObject != NULL) &&
|
||
#endif
|
||
((Scb->Header.PagingIoResource == NULL) ||
|
||
ExAcquireResourceExclusiveLite( Scb->Header.PagingIoResource, FALSE ))) {
|
||
|
||
|
||
KeWaitForSingleObject( &StreamFileCreationMutex, Executive, KernelMode, FALSE, NULL );
|
||
|
||
//
|
||
// Capture both file objects and clear the fields so no one else
|
||
// can access them.
|
||
//
|
||
|
||
if (CompressedStreamOnly) {
|
||
|
||
FileObject = NULL;
|
||
|
||
} else {
|
||
|
||
FileObject = Scb->FileObject;
|
||
Scb->FileObject = NULL;
|
||
|
||
//
|
||
// Clear the internal file name constant
|
||
//
|
||
|
||
NtfsClearInternalFilename( FileObject );
|
||
}
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
FileObjectC = Scb->Header.FileObjectC;
|
||
Scb->Header.FileObjectC = NULL;
|
||
#endif
|
||
|
||
KeReleaseMutant( &StreamFileCreationMutex, IO_NO_INCREMENT, FALSE, FALSE );
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
//
|
||
// Now dereference each file object.
|
||
//
|
||
|
||
if (FileObject != NULL) {
|
||
|
||
//
|
||
// We shouldn't be deleting the internal stream objects of the MFT & co, unless
|
||
// we are in the dismounting, restarting or mounting path.
|
||
//
|
||
|
||
ASSERT( (((PSCB) FileObject->FsContext)->Header.NodeTypeCode != NTFS_NTC_SCB_MFT) ||
|
||
FlagOn( Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) ||
|
||
FlagOn( Scb->Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT ) ||
|
||
!FlagOn( Scb->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) ||
|
||
Scb->Vcb->RootIndexScb == NULL );
|
||
|
||
if (FileObject->PrivateCacheMap != NULL) {
|
||
|
||
CcUninitializeCacheMap( FileObject,
|
||
(ForceClose ? &Li0 : NULL),
|
||
NULL );
|
||
}
|
||
|
||
ObDereferenceObject( FileObject );
|
||
Dereferenced = TRUE;
|
||
}
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
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;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
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 );
|
||
}
|
||
#ifdef MAPCOUNT_DBG
|
||
IrpContext->MapCount++;
|
||
#endif
|
||
|
||
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
|
||
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
|
||
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;
|
||
}
|
||
|
||
|
||
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;
|
||
PNTFS_ADVANCED_FCB_HEADER Header;
|
||
|
||
ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
||
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:
|
||
|
||
try {
|
||
|
||
PSCB Scb;
|
||
VBO StartingVbo;
|
||
LONGLONG ByteCount;
|
||
LONGLONG ByteRange;
|
||
BOOLEAN DoingIoAtEof = FALSE;
|
||
|
||
ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
Scb = (PSCB)(IrpSp->FileObject->FsContext);
|
||
Header = &(Scb->Header);
|
||
|
||
//
|
||
// Now synchronize with the FsRtl Header and Scb.
|
||
//
|
||
|
||
if (Header->PagingIoResource != NULL) {
|
||
|
||
StartingVbo = IrpSp->Parameters.Write.ByteOffset.QuadPart;
|
||
ByteCount = (LONGLONG) IrpSp->Parameters.Write.Length;
|
||
ByteRange = StartingVbo + ByteCount + PAGE_SIZE - 1;
|
||
ClearFlag( ((ULONG) ByteRange), PAGE_SIZE - 1 );
|
||
|
||
ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
|
||
//
|
||
// Now see if this is at EOF.
|
||
// Recursive flush will generate IO which ends on page boundary
|
||
// which is why we rounded the range
|
||
//
|
||
|
||
if (ByteRange > Header->ValidDataLength.QuadPart) {
|
||
|
||
//
|
||
// Mark that we are writing to EOF. If someone else is currently
|
||
// writing to EOF, wait for them.
|
||
//
|
||
|
||
ASSERT( ByteRange - StartingVbo < MAXULONG );
|
||
|
||
DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
|
||
NtfsWaitForIoAtEof( Header, (PLARGE_INTEGER)&StartingVbo, (ULONG)(ByteRange - StartingVbo) );
|
||
|
||
if (DoingIoAtEof) {
|
||
|
||
SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
|
||
|
||
#if (DBG || defined( NTFS_FREE_ASSERTS ))
|
||
((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
|
||
#endif
|
||
//
|
||
// Store this in the IrpContext until commit or post.
|
||
//
|
||
|
||
IrpContext->CleanupStructure = Scb;
|
||
}
|
||
}
|
||
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
}
|
||
|
||
CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress );
|
||
|
||
} finally {
|
||
|
||
if (Header->PagingIoResource != NULL) {
|
||
|
||
ExReleaseResourceLite( Header->PagingIoResource );
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
//
|
||
// Ignore errors. CC has already cleaned up his structures.
|
||
//
|
||
|
||
IrpContext->ExceptionStatus = STATUS_SUCCESS;
|
||
NtfsMinimumExceptionProcessing( IrpContext );
|
||
|
||
//
|
||
// 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,
|
||
IN OUT PLONGLONG CommittedFileSize OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
CommittedFileSize - If we write the file sizes and commit the
|
||
transaction then we want to let our caller know what
|
||
point to roll back file size on a subsequent failure. On entry
|
||
it has the size our caller wants to roll back the file size to.
|
||
On exit it has the new size to roll back to which takes into
|
||
account any updates to the file size which have been logged.
|
||
|
||
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;
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
IO_STATUS_BLOCK IoStatus;
|
||
#endif
|
||
|
||
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 = (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
|
||
SectorSize = Vcb->BytesPerSector;
|
||
|
||
//
|
||
// We may be able to simplify the zero operation (sparse file or when writing
|
||
// compressed) by deallocating large ranges of the file. Otherwise we have to
|
||
// generate zeroes for the entire range. If that is the case we want to split
|
||
// this operation up.
|
||
//
|
||
|
||
if ((ByteCount > MAX_ZERO_THRESHOLD) &&
|
||
!FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) &&
|
||
!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
|
||
|
||
ByteCount = MAX_ZERO_THRESHOLD;
|
||
CompleteZero = FALSE;
|
||
}
|
||
|
||
ZeroStart = StartingZero + (SectorSize - 1);
|
||
(ULONG)ZeroStart &= ~(SectorSize - 1);
|
||
|
||
BeyondZeroEnd = StartingZero + ByteCount + (SectorSize - 1);
|
||
(ULONG)BeyondZeroEnd &= ~(SectorSize - 1);
|
||
|
||
//
|
||
// We must flush the first compression unit in case it is partially populated
|
||
// in the compressed stream.
|
||
//
|
||
|
||
#ifdef COMPRESS_ON_WIRE
|
||
|
||
if ((Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
|
||
((StartingZero & (CompressionUnit - 1)) != 0)) {
|
||
|
||
(ULONG)StartingZero &= ~(CompressionUnit - 1);
|
||
CcFlushCache( &Scb->NonpagedScb->SegmentObjectC,
|
||
(PLARGE_INTEGER)&StartingZero,
|
||
CompressionUnit,
|
||
&IoStatus );
|
||
|
||
if (!NT_SUCCESS(IoStatus.Status)) {
|
||
NtfsNormalizeAndRaiseStatus( IrpContext, IoStatus.Status, STATUS_UNEXPECTED_IO_ERROR );
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If this is a sparse or 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 we are currently decompressing
|
||
// a compressed file we can't take this path.
|
||
//
|
||
|
||
if ((FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) ||
|
||
FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) &&
|
||
(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;
|
||
}
|
||
|
||
//
|
||
// Now delete all of the compression units in between.
|
||
//
|
||
|
||
//
|
||
// Calculate the start of the last compression unit in bytes.
|
||
//
|
||
|
||
Temp = BeyondZeroEnd;
|
||
(ULONG)Temp &= ~(CompressionUnit - 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 (!NtfsIsExclusiveScb( Scb )) {
|
||
|
||
NtfsAcquireExclusiveScb( IrpContext, Scb );
|
||
ScbAcquired = TRUE;
|
||
|
||
if (ARGUMENT_PRESENT( CommittedFileSize )) {
|
||
|
||
NtfsMungeScbSnapshot( IrpContext, Scb, *CommittedFileSize );
|
||
}
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Delete the space.
|
||
//
|
||
|
||
NtfsDeleteAllocation( IrpContext,
|
||
FileObject,
|
||
Scb,
|
||
LlClustersFromBytes( Vcb, ZeroStart ),
|
||
LlClustersFromBytesTruncate( Vcb, Temp ) - 1,
|
||
TRUE,
|
||
TRUE );
|
||
|
||
//
|
||
// If we didn't raise then update the Scb values for compressed files.
|
||
//
|
||
|
||
if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
||
Scb->ValidDataToDisk = Temp;
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
if (ARGUMENT_PRESENT( CommittedFileSize )) {
|
||
|
||
ASSERT( Scb->ScbSnapshot != NULL );
|
||
*CommittedFileSize = Scb->ScbSnapshot->FileSize;
|
||
}
|
||
|
||
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
|
||
|
||
NtfsReleaseFcb( IrpContext,
|
||
(PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
|
||
FCB,
|
||
ExclusiveFcbLinks ));
|
||
}
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
|
||
IRP_CONTEXT_FLAG_RELEASE_MFT );
|
||
|
||
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 );
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// If we were called to just zero part of a sector we are in trouble.
|
||
//
|
||
|
||
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;
|
||
|
||
//
|
||
// Move the rollback point up to include the range of zeroed
|
||
// data.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( CommittedFileSize )) {
|
||
|
||
if (BeyondZeroEnd > *CommittedFileSize) {
|
||
|
||
*CommittedFileSize = BeyondZeroEnd;
|
||
}
|
||
}
|
||
|
||
ASSERT( Scb->Header.ValidDataLength.QuadPart <= Scb->Header.FileSize.QuadPart );
|
||
|
||
ExReleaseFastMutex( Scb->Header.FastMutex );
|
||
NtfsCheckpointCurrentTransaction( IrpContext );
|
||
return FALSE;
|
||
}
|
||
|
||
return Finished;
|
||
}
|