summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/mcbsup.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/mcbsup.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/cntfs/mcbsup.c')
-rw-r--r--private/ntos/cntfs/mcbsup.c2610
1 files changed, 2610 insertions, 0 deletions
diff --git a/private/ntos/cntfs/mcbsup.c b/private/ntos/cntfs/mcbsup.c
new file mode 100644
index 000000000..330925812
--- /dev/null
+++ b/private/ntos/cntfs/mcbsup.c
@@ -0,0 +1,2610 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ McbSup.c
+
+Abstract:
+
+ This module implements the Ntfs Mcb package.
+
+Author:
+
+ Gary Kimura [GaryKi] 10-Sep-1994
+ Tom Miller [TomM]
+
+Revision History:
+
+--*/
+
+#include "NtfsProc.h"
+
+#define FIRST_RANGE ((PVOID)1)
+
+#ifndef NTFS_VERIFY_MCB
+#define NtfsVerifyNtfsMcb(M) NOTHING;
+#define NtfsVerifyUncompressedNtfsMcb(M,S,E) NOTHING;
+#endif
+
+//
+// Define a tag for general pool allocations from this module
+//
+
+#undef MODULE_POOL_TAG
+#define MODULE_POOL_TAG ('MFtN')
+
+//
+// Local procedure prototypes
+//
+
+ULONG
+NtfsMcbLookupArrayIndex (
+ IN PNTFS_MCB Mcb,
+ IN VCN Vcn
+ );
+
+VOID
+NtfsInsertNewRange (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN ULONG ArrayIndex,
+ IN BOOLEAN MakeNewRangeEmpty
+ );
+
+VOID
+NtfsCollapseRanges (
+ IN PNTFS_MCB Mcb,
+ IN ULONG StartingArrayIndex,
+ IN ULONG EndingArrayIndex
+ );
+
+VOID
+NtfsMcbCleanupLruQueue (
+ IN PVOID Parameter
+ );
+
+#ifdef NTFS_VERIFY_MCB
+VOID
+NtfsVerifyNtfsMcb (
+ IN PNTFS_MCB Mcb
+ );
+
+VOID
+NtfsVerifyUncompressedNtfsMcb (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN LONGLONG EndingVcn
+ );
+#endif
+
+BOOLEAN
+NtfsLockNtfsMcb (
+ IN PNTFS_MCB Mcb
+ );
+
+VOID
+NtfsUnlockNtfsMcb (
+ IN PNTFS_MCB Mcb
+ );
+
+//
+// Local macros to ASSERT that caller's resource is exclusive or restart is
+// underway.
+//
+
+#define ASSERT_STREAM_EXCLUSIVE(M) { \
+ ASSERT( FlagOn( ((PSCB) (M)->FcbHeader)->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) || \
+ ExIsResourceAcquiredExclusive((M)->FcbHeader->Resource )); \
+}
+
+//
+// Local macros to enqueue and dequeue elements from the lru queue
+//
+
+#define NtfsMcbEnqueueLruEntry(M,E) { \
+ InsertTailList( &NtfsMcbLruQueue, &(E)->LruLinks ); \
+ NtfsMcbCurrentLevel += 1; \
+}
+
+#define NtfsMcbDequeueLruEntry(M,E) { \
+ if ((E)->LruLinks.Flink != NULL) { \
+ RemoveEntryList( &(E)->LruLinks ); \
+ NtfsMcbCurrentLevel -= 1; \
+ } \
+}
+
+//
+// Local macro to unload a single array entry
+//
+
+#define UnloadEntry(M,I) { \
+ PNTFS_MCB_ENTRY _Entry; \
+ _Entry = (M)->NtfsMcbArray[(I)].NtfsMcbEntry; \
+ (M)->NtfsMcbArray[(I)].NtfsMcbEntry = NULL; \
+ if (_Entry != NULL) { \
+ ExAcquireFastMutex( &NtfsMcbFastMutex ); \
+ NtfsMcbDequeueLruEntry( Mcb, _Entry ); \
+ ExReleaseFastMutex( &NtfsMcbFastMutex ); \
+ FsRtlUninitializeLargeMcb( &_Entry->LargeMcb ); \
+ if ((M)->NtfsMcbArraySize != 1) { \
+ NtfsFreePool( _Entry ); \
+ } \
+ } \
+}
+
+
+VOID
+NtfsInitializeNtfsMcb (
+ IN PNTFS_MCB Mcb,
+ IN PFSRTL_ADVANCED_FCB_HEADER FcbHeader,
+ IN PNTFS_MCB_INITIAL_STRUCTS McbStructs,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a new Ntfs Mcb structure.
+
+Arguments:
+
+ Mcb - Supplies the Mcb being initialized
+
+ FcbHeader - Supplies a pointer to the Fcb header containing
+ the resource to grab when accessing the Mcb
+
+ McbStructs - Initial allocation typically coresident in another
+ structure to handle initial structures for small and
+ medium files. This structure should be initially zeroed.
+
+ PoolType - Supplies the type of pool to use when
+ allocating mapping information storage
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PNTFS_MCB_ARRAY Array;
+
+ RtlZeroMemory( McbStructs, sizeof(NTFS_MCB_INITIAL_STRUCTS) );
+
+ //
+ // Initialize the fcb header field of the mcb
+ //
+
+ Mcb->FcbHeader = FcbHeader;
+
+ //
+ // Initialize the pool type
+ //
+
+ Mcb->PoolType = PoolType;
+
+ //
+ // Now initialize the initial array element
+ //
+
+ Mcb->NtfsMcbArray = Array = &McbStructs->Phase1.SingleMcbArrayEntry;
+ Mcb->NtfsMcbArraySize = 1;
+ Mcb->NtfsMcbArraySizeInUse = 1;
+ Mcb->FastMutex = FcbHeader->FastMutex;
+
+ //
+ // Initialize the first array entry.
+ //
+
+ Array[0].StartingVcn = 0;
+ Array[0].EndingVcn = -1;
+
+ //
+ // And return to our caller
+ //
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return;
+}
+
+
+VOID
+NtfsUninitializeNtfsMcb (
+ IN PNTFS_MCB Mcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine uninitializes an Ntfs Mcb structure.
+
+Arguments:
+
+ Mcb - Supplies the Mcb being decommissioned
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ PNTFS_MCB_ENTRY Entry;
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Take out the global mutex
+ //
+
+ ExAcquireFastMutex( &NtfsMcbFastMutex );
+
+ //
+ // Deallocate the mcb array if it exists. For every entry in the array
+ // if the mcb entry is not null then remove the entry from the lru
+ // queue, uninitialize the large mcb, and free the pool.
+ //
+
+ if (Mcb->NtfsMcbArray != NULL) {
+
+ for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
+
+ if ((Entry = Mcb->NtfsMcbArray[i].NtfsMcbEntry) != NULL) {
+
+ //
+ // Remove the entry from the lru queue
+ //
+
+ NtfsMcbDequeueLruEntry( Mcb, Entry );
+
+ //
+ // Now release the entry
+ //
+
+ FsRtlUninitializeLargeMcb( &Entry->LargeMcb );
+
+ //
+ // We can tell from the array count whether this is
+ // the initial entry and does not need to be deallocated.
+ //
+
+ if (Mcb->NtfsMcbArraySize > 1) {
+ NtfsFreePool( Entry );
+ }
+ }
+ }
+
+ //
+ // We can tell from the array count whether this is
+ // the initial array entry(s) and do not need to be deallocated.
+ //
+
+
+ if (Mcb->NtfsMcbArraySize > 3) {
+ NtfsFreePool( Mcb->NtfsMcbArray );
+ }
+
+ Mcb->NtfsMcbArray = NULL;
+
+ //
+ // Clear the fast mutex field.
+ //
+
+ Mcb->FastMutex = NULL;
+ }
+
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+
+ULONG
+NtfsNumberOfRangesInNtfsMcb (
+ IN PNTFS_MCB Mcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the total number of ranges stored in
+ the mcb
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+Return Value:
+
+ ULONG - The number of ranges mapped by the input mcb
+
+--*/
+
+{
+ ASSERT_STREAM_EXCLUSIVE(Mcb);
+
+ //
+ // Our answer is the number of ranges in use in the mcb
+ //
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return Mcb->NtfsMcbArraySizeInUse;
+}
+
+
+BOOLEAN
+NtfsNumberOfRunsInRange (
+ IN PNTFS_MCB Mcb,
+ IN PVOID RangePtr,
+ OUT PULONG NumberOfRuns
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the total number of runs stored withing a range
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+ RangePtr - Supplies the range to being queried
+
+ NumberOrRuns - Returns the number of run in the specified range
+ but only if the range is loaded
+
+Return Value:
+
+ BOOLEAN - TRUE if the range is loaded and then output variable
+ is valid and FALSE if the range is not loaded.
+
+--*/
+
+{
+ VCN TempVcn;
+ LCN TempLcn;
+ PNTFS_MCB_ENTRY Entry = (PNTFS_MCB_ENTRY)RangePtr;
+
+ //
+ // Null RangePtr means first range
+ //
+
+ if (Entry == FIRST_RANGE) {
+ Entry = Mcb->NtfsMcbArray[0].NtfsMcbEntry;
+
+ //
+ // If not loaded, return FALSE
+ //
+
+ if (Entry == NULL) {
+ return FALSE;
+ }
+ }
+
+ ASSERT_STREAM_EXCLUSIVE(Mcb);
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ ASSERT( Mcb == Entry->NtfsMcb );
+
+ *NumberOfRuns = FsRtlNumberOfRunsInLargeMcb( &Entry->LargeMcb );
+
+ //
+ // Check if the current entry ends with a hole and increment the run count
+ // to reflect this. Detect the case where the range has length 0 for a
+ // file with no allocation. EndingVcn will be less than the starting Vcn
+ // in this case.
+ //
+
+ if (!FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, &TempVcn, &TempLcn )) {
+
+ //
+ // If this is a non-zero length range then add one for the implied hole.
+ //
+
+ if (Entry->NtfsMcbArray->EndingVcn >= Entry->NtfsMcbArray->StartingVcn) {
+
+ *NumberOfRuns += 1;
+ }
+
+ //
+ // There is an entry then check if it reaches the end boundary of the range.
+ //
+
+ } else if (TempVcn != (Entry->NtfsMcbArray->EndingVcn - Entry->NtfsMcbArray->StartingVcn)) {
+
+ *NumberOfRuns += 1;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+NtfsLookupLastNtfsMcbEntry (
+ IN PNTFS_MCB Mcb,
+ OUT PLONGLONG Vcn,
+ OUT PLONGLONG Lcn
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the last mapping stored in the mcb
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+ Vcn - Receives the Vcn of the last mapping
+
+ Lcn - Receives the Lcn corresponding to the Vcn
+
+Return Value:
+
+ BOOLEAN - TRUE if the mapping exist and FALSE if no mapping has been
+ defined or it is unloaded
+
+--*/
+
+{
+ PNTFS_MCB_ENTRY Entry;
+ LONGLONG StartingVcn;
+
+ ASSERT_STREAM_EXCLUSIVE(Mcb);
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Get the last entry and compute its starting vcn, and make sure
+ // the entry is valid
+ //
+
+ if ((Entry = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].NtfsMcbEntry) == NULL) {
+
+ return FALSE;
+ }
+
+ StartingVcn = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].StartingVcn;
+
+ //
+ // Otherwise lookup the last entry and compute the real vcn
+ //
+
+ if (FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, Vcn, Lcn )) {
+
+ *Vcn += StartingVcn;
+
+ } else {
+
+ *Vcn = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].EndingVcn;
+ *Lcn = UNUSED_LCN;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+NtfsLookupNtfsMcbEntry (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG Vcn,
+ OUT PLONGLONG Lcn OPTIONAL,
+ OUT PLONGLONG CountFromLcn OPTIONAL,
+ OUT PLONGLONG StartingLcn OPTIONAL,
+ OUT PLONGLONG CountFromStartingLcn OPTIONAL,
+ OUT PVOID *RangePtr OPTIONAL,
+ OUT PULONG RunIndex OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to query mapping information
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+ Vcn - Supplies the Vcn being queried
+
+ Lcn - Optionally receives the lcn corresponding to the input vcn
+
+ CountFromLcn - Optionally receives the number of clusters following
+ the lcn in the run
+
+ StartingLcn - Optionally receives the start of the run containing the
+ input vcn
+
+ CountFromStartingLcn - Optionally receives the number of clusters in
+ the entire run
+
+ RangePtr - Optionally receives the index for the range that we're returning
+
+ RunIndex - Optionally receives the index for the run within the range that
+ we're returning
+
+Return Value:
+
+ BOOLEAN - TRUE if the mapping exists and FALSE if it doesn't exist
+ or if it is unloaded.
+
+--*/
+
+{
+ ULONG LocalRangeIndex;
+
+ PNTFS_MCB_ENTRY Entry;
+
+ NtfsAcquireNtfsMcbMutex( Mcb );
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Locate the array entry that has the hit for the input vcn, and
+ // make sure it is valid. Also set the output range index if present
+ //
+
+ LocalRangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
+
+ //
+ // Now lookup the large mcb entry. The Vcn we pass in is
+ // biased by the starting vcn. If we miss then we'll just return false
+ //
+
+ if (((Entry = Mcb->NtfsMcbArray[LocalRangeIndex].NtfsMcbEntry) == NULL) ||
+ (Vcn > Entry->NtfsMcbArray->EndingVcn)) {
+
+ if (ARGUMENT_PRESENT(RangePtr)) {
+
+ *RangePtr = (PVOID)Entry;
+
+ //
+ // If this is the first range, always normalize back to the reserved pointer,
+ // since this is the only range which can move if we split out of our
+ // initial static allocation!
+ //
+
+ if (LocalRangeIndex == 0) {
+ *RangePtr = FIRST_RANGE;
+ }
+ }
+
+ NtfsReleaseNtfsMcbMutex( Mcb );
+
+ return FALSE;
+ }
+
+ if (!FsRtlLookupLargeMcbEntry( &Entry->LargeMcb,
+ Vcn - Mcb->NtfsMcbArray[LocalRangeIndex].StartingVcn,
+ Lcn,
+ CountFromLcn,
+ StartingLcn,
+ CountFromStartingLcn,
+ RunIndex )) {
+
+ //
+ // If we go off the end of the Mcb, but are in the range, then we
+ // return a hole to the end of the range.
+ //
+
+ if (ARGUMENT_PRESENT(Lcn)) {
+ *Lcn = UNUSED_LCN;
+ }
+
+ if (ARGUMENT_PRESENT(CountFromLcn)) {
+ *CountFromLcn = Mcb->NtfsMcbArray[LocalRangeIndex].EndingVcn - Vcn + 1;
+ }
+
+ if (ARGUMENT_PRESENT(StartingLcn)) {
+ *StartingLcn = UNUSED_LCN;
+ }
+
+ ASSERTMSG("Brian's bogus CountFromStartingLcn is not implemented\n",
+ !ARGUMENT_PRESENT(CountFromStartingLcn));
+
+ if (ARGUMENT_PRESENT(RunIndex)) {
+ *RunIndex = FsRtlNumberOfRunsInLargeMcb( &Entry->LargeMcb );
+ }
+ }
+
+ if (ARGUMENT_PRESENT(RangePtr)) {
+
+ *RangePtr = (PVOID)Entry;
+
+ //
+ // If this is the first range, always normalize back to the reserved pointer,
+ // since this is the only range which can move if we split out of our
+ // initial static allocation!
+ //
+
+ if (LocalRangeIndex == 0) {
+ *RangePtr = FIRST_RANGE;
+ }
+ }
+
+ //
+ // Now move this entry to the tail of the lru queue.
+ // We need to take out the global mutex to do this.
+ // Only do this if he is already in the queue - we can
+ // deadlock if we take a fault in the paging file path.
+ //
+
+ if (Entry->LruLinks.Flink != NULL) {
+
+ if (ExTryToAcquireFastMutex( &NtfsMcbFastMutex )) {
+
+ NtfsMcbDequeueLruEntry( Mcb, Entry );
+ NtfsMcbEnqueueLruEntry( Mcb, Entry );
+
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+ }
+ }
+
+ NtfsReleaseNtfsMcbMutex( Mcb );
+
+ return TRUE;
+}
+
+
+BOOLEAN
+NtfsGetNextNtfsMcbEntry (
+ IN PNTFS_MCB Mcb,
+ IN PVOID *RangePtr,
+ IN ULONG RunIndex,
+ OUT PLONGLONG Vcn,
+ OUT PLONGLONG Lcn,
+ OUT PLONGLONG Count
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the range denoted by the type index values
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+ RangePtr - Supplies the pointer to the range being queried, or NULL for the first one,
+ returns next range
+
+ RunIndex - Supplies the index within then being queried, or MAXULONG for first in next
+
+ Vcn - Receives the starting Vcn of the run being returned
+
+ Lcn - Receives the starting Lcn of the run being returned or unused
+ lbn value of -1
+
+ Count - Receives the number of clusters within this run
+
+Return Value:
+
+ BOOLEAN - TRUE if the two input indices are valid and FALSE if the
+ the index are not valid or if the range is not loaded
+
+--*/
+
+{
+ PNTFS_MCB_ENTRY Entry = (PNTFS_MCB_ENTRY)*RangePtr;
+ BOOLEAN Result = FALSE;
+
+ NtfsAcquireNtfsMcbMutex( Mcb );
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ try {
+
+ //
+ // Null RangePtr means first range
+ //
+
+ if (Entry == FIRST_RANGE) {
+ Entry = Mcb->NtfsMcbArray[0].NtfsMcbEntry;
+ }
+
+ //
+ // If there is no entry 0, get out.
+ //
+
+ if (Entry == NULL) {
+
+ try_return(Result = FALSE);
+ }
+
+ //
+ // RunIndex of MAXULONG means first of next
+ //
+
+ if (RunIndex == MAXULONG) {
+
+ //
+ // If we are already in the last range, get out.
+ //
+
+ if (Entry->NtfsMcbArray == (Mcb->NtfsMcbArray + Mcb->NtfsMcbArraySizeInUse - 1)) {
+
+ try_return(Result = FALSE);
+ }
+
+ *RangePtr = Entry = (Entry->NtfsMcbArray + 1)->NtfsMcbEntry;
+ RunIndex = 0;
+ }
+
+ //
+ // If there is no next entry, get out.
+ //
+
+ if (Entry == NULL) {
+
+ try_return(Result = FALSE);
+ }
+
+ ASSERT( Mcb == Entry->NtfsMcb );
+
+ //
+ // Lookup the large mcb entry. If we get a miss then the we're
+ // beyond the end of the ntfs mcb and should return false
+ //
+
+ if (!FsRtlGetNextLargeMcbEntry( &Entry->LargeMcb, RunIndex, Vcn, Lcn, Count )) {
+
+ //
+ // Our caller should only be off by one or two (if there is
+ // a hole) runs.
+ //
+
+ ASSERT(RunIndex <= (FsRtlNumberOfRunsInLargeMcb(&Entry->LargeMcb) + 1));
+
+ //
+ // Get the first Vcn past the last Vcn in a run. It is -1 if there
+ // are no runs.
+ //
+
+ if (!FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, Vcn, Lcn )) {
+
+ *Vcn = -1;
+ }
+
+ *Vcn += Entry->NtfsMcbArray->StartingVcn + 1;
+
+ //
+ // If that one is beyond the ending Vcn, then get out.
+ // Otherwise there is a hole at the end of the range, and we
+ // must return that when he is reading one index beyond the
+ // last run. If we have a run index beyond that, then it is
+ // time to return FALSE as well.
+ //
+
+ if ((*Vcn > Entry->NtfsMcbArray->EndingVcn) ||
+ (RunIndex > FsRtlNumberOfRunsInLargeMcb(&Entry->LargeMcb))) {
+
+ try_return(Result = FALSE);
+ }
+
+ //
+ // If we go off the end of the Mcb, but are in the range, then we
+ // return a hole to the end of the range.
+ //
+
+ *Lcn = UNUSED_LCN;
+ *Count = Entry->NtfsMcbArray->EndingVcn - *Vcn + 1;
+
+ } else {
+
+ //
+ // Otherwise we have a hit on the large mcb and need to bias the returned
+ // vcn by the starting vcn value for this range.
+ //
+
+ *Vcn = *Vcn + Entry->NtfsMcbArray->StartingVcn;
+ }
+
+ //
+ // Make certain we aren't returning a VCN that maps over to
+ // the next range.
+ //
+
+ ASSERT(*Vcn - 1 != Entry->NtfsMcbArray->EndingVcn);
+
+ Result = TRUE;
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ NtfsReleaseNtfsMcbMutex( Mcb );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+NtfsSplitNtfsMcb (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG Vcn,
+ IN LONGLONG Amount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine splits an mcb
+
+Arguments:
+
+ Mcb - Supplies the Mcb being maniuplated
+
+ Vcn - Supplies the Vcn to be shifted
+
+ Amount - Supplies the amount to shift by
+
+Return Value:
+
+ BOOLEAN - TRUE if worked okay and FALSE otherwise
+
+--*/
+
+{
+ ULONG RangeIndex;
+ PNTFS_MCB_ENTRY Entry;
+ ULONG i;
+
+ ASSERT_STREAM_EXCLUSIVE(Mcb);
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Locate the array entry that has the hit for the input vcn
+ //
+
+ RangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
+
+ Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
+
+ //
+ // Now if the entry is not null then we have to call the large
+ // mcb package to split the mcb. Bias the vcn by the starting vcn
+ //
+
+ if (Entry != NULL) {
+
+ if (!FsRtlSplitLargeMcb( &Entry->LargeMcb,
+ Vcn - Mcb->NtfsMcbArray[RangeIndex].StartingVcn,
+ Amount )) {
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return FALSE;
+ }
+ }
+
+ //
+ // Even if the entry is null we will march through the rest of our ranges
+ // updating the ending vcn and starting vcn as we go. We will update the
+ // ending vcn for the range we split and only update the starting vcn
+ // for the last entry, because its ending vcn is already max long long
+ //
+
+ for (i = RangeIndex + 1; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
+
+ Mcb->NtfsMcbArray[i - 1].EndingVcn += Amount;
+ Mcb->NtfsMcbArray[i].StartingVcn += Amount;
+ }
+
+ //
+ // And grow the last range unless it would wrap.
+ //
+
+ if ((Mcb->NtfsMcbArray[i - 1].EndingVcn + Amount) > Mcb->NtfsMcbArray[i - 1].EndingVcn) {
+ Mcb->NtfsMcbArray[i - 1].EndingVcn += Amount;
+ }
+
+ //
+ // Then return to our caller
+ //
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return TRUE;
+}
+
+
+VOID
+NtfsRemoveNtfsMcbEntry (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN LONGLONG Count
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes an range of mappings from the Mcb. After
+ the call the mapping for the range will be a hole. It is an
+ error to call this routine with the mapping range being removed
+ also being unloaded.
+
+Arguments:
+
+ Mcb - Supplies the Mcb being maniuplated
+
+ StartingVcn - Supplies the starting Vcn to remove
+
+ Count - Supplies the number of mappings to remove
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONGLONG Vcn;
+ LONGLONG RunLength;
+ LONGLONG RemainingCount;
+
+ ULONG RangeIndex;
+ PNTFS_MCB_ENTRY Entry;
+ VCN EntryStartingVcn;
+ VCN EntryEndingVcn;
+
+ ASSERT_STREAM_EXCLUSIVE(Mcb);
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Loop through the range of vcn's that we need to remove
+ //
+
+ for (Vcn = StartingVcn, RemainingCount = Count;
+ Vcn < StartingVcn + Count;
+ Vcn += RunLength, RemainingCount -= RunLength) {
+
+ //
+ // Locate the array entry that has the hit for the vcn
+ //
+
+ RangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
+
+ Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
+ EntryStartingVcn = Mcb->NtfsMcbArray[RangeIndex].StartingVcn;
+ EntryEndingVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
+
+ //
+ // Compute how much to delete from the entry. We will delete to
+ // to end of the entry or as much as count is remaining
+ //
+
+ RunLength = EntryEndingVcn - Vcn + 1;
+
+ //
+ // If the Mcb is set up correctly, the only way we can get
+ // RunLength == 0 is if the Mcb is completely empty. Assume
+ // that this is error recovery, and that it is ok.
+ //
+
+ if ((Entry == NULL) || (RunLength == 0)) {
+ break;
+ }
+
+ //
+ // If that is too much, then just delete what we need.
+ //
+
+ if ((ULONGLONG)RunLength > (ULONGLONG)RemainingCount) { RunLength = RemainingCount; }
+
+ //
+ // Now remove the mapping from the large mcb, bias the vcn
+ // by the start of the range
+ //
+
+ FsRtlRemoveLargeMcbEntry( &Entry->LargeMcb, Vcn - EntryStartingVcn, RunLength );
+ }
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return;
+}
+
+
+BOOLEAN
+NtfsAddNtfsMcbEntry (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG Vcn,
+ IN LONGLONG Lcn,
+ IN LONGLONG RunCount,
+ IN BOOLEAN AlreadySynchronized
+ )
+
+/*++
+
+Routine Description:
+
+ This routine add a new entry to a Mcb
+
+Arguments:
+
+ Mcb - Supplies the Mcb being modified
+
+ Vcn - Supplies the Vcn that we are providing a mapping for
+
+ Lcn - Supplies the Lcn corresponding to the input Vcn if run count is non zero
+
+ RunCount - Supplies the size of the run following the hole
+
+ AlreadySynchronized - Indicates if the caller has already acquired the mcb mutex
+
+Return Value:
+
+ BOOLEAN - TRUE if the mapping was added successfully and FALSE otherwise
+
+--*/
+
+{
+ LONGLONG LocalVcn;
+ LONGLONG LocalLcn;
+ LONGLONG RunLength;
+ LONGLONG RemainingCount;
+
+ ULONG RangeIndex;
+ PNTFS_MCB_ENTRY Entry;
+ PNTFS_MCB_ENTRY NewEntry = NULL;
+ LONGLONG EntryStartingVcn;
+ LONGLONG EntryEndingVcn;
+
+ BOOLEAN Result = FALSE;
+
+ if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ try {
+
+ //
+ // Loop through the range of vcn's that we need to add
+ //
+
+ for (LocalVcn = Vcn, LocalLcn = Lcn, RemainingCount = RunCount;
+ LocalVcn < Vcn + RunCount;
+ LocalVcn += RunLength, LocalLcn += RunLength, RemainingCount -= RunLength) {
+
+ //
+ // Locate the array entry that has the hit for the vcn
+ //
+
+ RangeIndex = NtfsMcbLookupArrayIndex(Mcb, LocalVcn);
+
+ Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
+ EntryStartingVcn = Mcb->NtfsMcbArray[RangeIndex].StartingVcn;
+
+ //
+ // Now if the entry doesn't exist then we'll need to create one
+ //
+
+ if (Entry == NULL) {
+
+ //
+ // See if we need to get the first entry in the initial structs.
+ //
+
+ if (Mcb->NtfsMcbArraySize == 1) {
+ Entry = &CONTAINING_RECORD(&Mcb->NtfsMcbArray[0],
+ NTFS_MCB_INITIAL_STRUCTS,
+ Phase1.SingleMcbArrayEntry)->Phase1.McbEntry;
+
+ //
+ // Allocate pool and initialize the fields in of the entry
+ //
+
+ } else {
+ NewEntry =
+ Entry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
+ }
+
+ //
+ // Initialize the entry but don't put into the Mcb array until
+ // initialization is complete.
+ //
+
+ Entry->NtfsMcb = Mcb;
+ Entry->NtfsMcbArray = &Mcb->NtfsMcbArray[RangeIndex];
+ FsRtlInitializeLargeMcb( &Entry->LargeMcb, Mcb->PoolType );
+
+ //
+ // Now put the entry into the lru queue under the protection of
+ // the global mutex
+ //
+
+ ExAcquireFastMutex( &NtfsMcbFastMutex );
+
+ //
+ // Only put paged Mcb entries in the queue.
+ //
+
+ if (Mcb->PoolType == PagedPool) {
+ NtfsMcbEnqueueLruEntry( Mcb, Entry );
+ }
+
+ //
+ // Now that the initialization is complete we can store
+ // this entry in the Mcb array. This will now be cleaned
+ // up with the Scb if there is a future error.
+ //
+
+ Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry = Entry;
+ NewEntry = NULL;
+
+ //
+ // Check if we should fire off the cleanup lru queue work item
+ //
+
+ if ((NtfsMcbCurrentLevel > NtfsMcbHighWaterMark) && !NtfsMcbCleanupInProgress) {
+
+ NtfsMcbCleanupInProgress = TRUE;
+
+ ExInitializeWorkItem( &NtfsMcbWorkItem, NtfsMcbCleanupLruQueue, NULL );
+
+ ExQueueWorkItem( &NtfsMcbWorkItem, CriticalWorkQueue );
+ }
+
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+ }
+
+ //
+ // Get out if he is trying to add a hole. At least we created the LargeMcb
+ //
+
+ if (Lcn == UNUSED_LCN) {
+ try_return( Result = TRUE );
+ }
+
+ //
+ // If this request goes beyond the end of the range,
+ // and it is the last range, and we will simply
+ // grow it.
+ //
+
+ EntryEndingVcn = LocalVcn + RemainingCount - 1;
+
+ if ((EntryEndingVcn > Mcb->NtfsMcbArray[RangeIndex].EndingVcn) &&
+ ((RangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse)) {
+
+ Mcb->NtfsMcbArray[RangeIndex].EndingVcn = EntryEndingVcn;
+
+ //
+ // Otherwise, just insert enough of this run to go to the end
+ // of the range.
+ //
+
+ } else {
+ EntryEndingVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
+ }
+
+ //
+ // At this point the entry exists so now compute how much to add
+ // We will add to end of the entry or as much as count allows us
+ //
+
+ RunLength = EntryEndingVcn - LocalVcn + 1;
+
+ if (((ULONGLONG)RunLength) > ((ULONGLONG)RemainingCount)) { RunLength = RemainingCount; }
+
+ //
+ // Now add the mapping from the large mcb, bias the vcn
+ // by the start of the range
+ //
+
+ ASSERT( (LocalVcn - EntryStartingVcn) >= 0 );
+
+ if (!FsRtlAddLargeMcbEntry( &Entry->LargeMcb,
+ LocalVcn - EntryStartingVcn,
+ LocalLcn,
+ RunLength )) {
+
+ try_return( Result = FALSE );
+ }
+ }
+
+ Result = TRUE;
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
+
+ if (NewEntry != NULL) { NtfsFreePool( NewEntry ); }
+ }
+
+ return Result;
+}
+
+
+VOID
+NtfsUnloadNtfsMcbRange (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN LONGLONG EndingVcn,
+ IN BOOLEAN TruncateOnly,
+ IN BOOLEAN AlreadySynchronized
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unloads the mapping stored in the Mcb. After
+ the call everything from startingVcn and endingvcn is now unmapped and unknown.
+
+Arguments:
+
+ Mcb - Supplies the Mcb being manipulated
+
+ StartingVcn - Supplies the first Vcn which is no longer being mapped
+
+ EndingVcn - Supplies the last vcn to be unloaded
+
+ TruncateOnly - Supplies TRUE if last affected range should only be
+ truncated, or FALSE if it should be unloaded (as during
+ error recovery)
+
+ AlreadySynchronized - Supplies TRUE if our caller already owns the Mcb mutex.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG StartingRangeIndex;
+ ULONG EndingRangeIndex;
+
+ ULONG i;
+
+ if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
+
+ NtfsVerifyNtfsMcb(Mcb);
+ NtfsVerifyUncompressedNtfsMcb(Mcb,StartingVcn,EndingVcn);
+
+ //
+ // Get the starting and ending range indices for this call
+ //
+
+ StartingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, StartingVcn );
+ EndingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, EndingVcn );
+
+ //
+ // Use try finally to enforce common termination processing.
+ //
+
+ try {
+
+ //
+ // For all nonpaged Mcbs, just unload all ranges touched by the
+ // unload range, and collapse with any unloaded neighbors.
+ //
+
+ if (Mcb->PoolType == PagedPool) {
+
+ //
+ // Handle truncate case. The first test insures that we only truncate
+ // the Mcb were were initialized with (we cannot deallocate it).
+ //
+ // Also only truncate if ending is MAXLONGLONG and we are not eliminating
+ // the entire range, because that is the common truncate case, and we
+ // do not want to unload the last range every time we truncate on close.
+ //
+
+ if (((StartingRangeIndex == 0) && (Mcb->NtfsMcbArraySizeInUse == 1))
+
+ ||
+
+ (TruncateOnly && (StartingVcn != Mcb->NtfsMcbArray[StartingRangeIndex].StartingVcn))) {
+
+ //
+ // If this is not a truncate call, make sure to eliminate the
+ // entire range.
+ //
+
+ if (!TruncateOnly) {
+ StartingVcn = 0;
+ }
+
+ if (Mcb->NtfsMcbArray[StartingRangeIndex].NtfsMcbEntry != NULL) {
+
+ FsRtlTruncateLargeMcb( &Mcb->NtfsMcbArray[StartingRangeIndex].NtfsMcbEntry->LargeMcb,
+ StartingVcn - Mcb->NtfsMcbArray[StartingRangeIndex].StartingVcn );
+ }
+
+ Mcb->NtfsMcbArray[StartingRangeIndex].EndingVcn = StartingVcn - 1;
+
+ StartingRangeIndex += 1;
+ }
+
+ //
+ // Unload entries that are beyond the starting range index
+ //
+
+ for (i = StartingRangeIndex; i <= EndingRangeIndex; i += 1) {
+
+ UnloadEntry( Mcb, i );
+ }
+
+ //
+ // If there is a preceding unloaded range, we must collapse him too.
+ //
+
+ if ((StartingRangeIndex != 0) &&
+ (Mcb->NtfsMcbArray[StartingRangeIndex - 1].NtfsMcbEntry == NULL)) {
+
+ StartingRangeIndex -= 1;
+ }
+
+ //
+ // If there is a subsequent unloaded range, we must collapse him too.
+ //
+
+ if ((EndingRangeIndex != (Mcb->NtfsMcbArraySizeInUse - 1)) &&
+ (Mcb->NtfsMcbArray[EndingRangeIndex + 1].NtfsMcbEntry == NULL)) {
+
+ EndingRangeIndex += 1;
+ }
+
+ //
+ // Now collapse empty ranges.
+ //
+
+ if (StartingRangeIndex < EndingRangeIndex) {
+ NtfsCollapseRanges( Mcb, StartingRangeIndex, EndingRangeIndex );
+ }
+
+ try_return(NOTHING);
+ }
+
+ //
+ // For nonpaged Mcbs, there is only one range and we truncate it.
+ //
+
+ ASSERT((StartingRangeIndex | EndingRangeIndex) == 0);
+
+ if (Mcb->NtfsMcbArray[0].NtfsMcbEntry != NULL) {
+
+ FsRtlTruncateLargeMcb( &Mcb->NtfsMcbArray[0].NtfsMcbEntry->LargeMcb, StartingVcn );
+ }
+
+ Mcb->NtfsMcbArray[0].EndingVcn = StartingVcn - 1;
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ //
+ // Truncate all unused entries from the end by dropping ArraySizeInUse
+ // to be the index of the last loaded entry + 1.
+ //
+
+ for (i = Mcb->NtfsMcbArraySizeInUse - 1;
+ (Mcb->NtfsMcbArray[i].NtfsMcbEntry == NULL);
+ i--) {
+
+ //
+ // If the first range is unloaded, set it to its initial state
+ // (empty) and break out.
+ //
+
+ if (i==0) {
+ Mcb->NtfsMcbArray[0].EndingVcn = -1;
+ break;
+ }
+ }
+ Mcb->NtfsMcbArraySizeInUse = i + 1;
+
+ //
+ // See if we broke anything.
+ //
+
+ NtfsVerifyNtfsMcb(Mcb);
+ NtfsVerifyUncompressedNtfsMcb(Mcb,StartingVcn,EndingVcn);
+
+ if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
+ }
+
+ return;
+}
+
+
+VOID
+NtfsDefineNtfsMcbRange (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN LONGLONG EndingVcn,
+ IN BOOLEAN AlreadySynchronized
+ )
+
+/*++
+
+Routine Description:
+
+ This routine splits an existing range within the Mcb into two ranges
+
+Arguments:
+
+ Mcb - Supplies the Mcb being modified
+
+ StartingVcn - Supplies the beginning of the new range being split
+
+ EndingVcn - Supplies the ending vcn to include in this new range
+
+ AlreadySynchronized - Indicates if the caller has already acquired the mcb mutex
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG StartingRangeIndex, EndingRangeIndex;
+
+ if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Make sure we're of the right pool type
+ //
+ // If the ending vcn is less than or equal to the starting vcn then we will no op
+ // this call
+ //
+
+ if ((Mcb->PoolType != PagedPool) || (EndingVcn < StartingVcn)) {
+
+ if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
+
+ return;
+ }
+
+ try {
+
+ PNTFS_MCB_ARRAY Array1;
+ PNTFS_MCB_ARRAY Array2;
+ PNTFS_MCB_ENTRY Entry1;
+ PNTFS_MCB_ENTRY Entry2;
+
+ //
+ // Lookup the index for the starting Vcn and make sure it is equivalent to the ending
+ // range index
+ //
+
+ StartingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, StartingVcn );
+ EndingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, EndingVcn );
+ Array1 = &Mcb->NtfsMcbArray[StartingRangeIndex];
+ Array2 = &Mcb->NtfsMcbArray[EndingRangeIndex];
+ Entry1 = Array1->NtfsMcbEntry;
+ Entry2 = Array2->NtfsMcbEntry;
+
+ //
+ // We handle two cases where the specified range overlaps two
+ // existing ranges.
+ //
+
+ if (EndingRangeIndex == (StartingRangeIndex + 1)) {
+
+ //
+ // If an existing range wants to grow into the next range, and the next
+ // range is unloaded, then make it happen.
+ //
+
+ if (Array2->NtfsMcbEntry == NULL) {
+
+ //
+ // Grow the first range and shrink the second range.
+ //
+
+ Array1->EndingVcn = EndingVcn;
+ Array2->StartingVcn = EndingVcn + 1;
+
+ //
+ // If we did not empty the second range, NULL out Array2,
+ // so that we will not eliminate a range below.
+ //
+
+ if (EndingVcn < Array2->EndingVcn) {
+ Array2 = NULL;
+ }
+
+ //
+ // Otherwise, we will split the second range, and move the entries
+ // from the end of the first range into the start of the second range.
+ // (Other optimizations in Ntfs are designed to make sure we do not
+ // end up sliding too much stuff around!)
+ //
+
+ } else {
+
+ ULONG Index;
+ VCN Vcn;
+ LCN Lcn;
+ LONGLONG Count;
+ VCN StartingVcn1 = Array1->StartingVcn;
+ BOOLEAN MoreEntries;
+
+ //
+ // Both Mcbs better be there.
+ //
+
+ ASSERT((Entry1 != NULL) && (Entry2 != NULL));
+
+ //
+ // Make room in the second Mcb for the entries we will move into there.
+ //
+
+ FsRtlSplitLargeMcb( &Entry2->LargeMcb,
+ 0,
+ (ULONG)(Array2->StartingVcn - StartingVcn) );
+
+ //
+ // Now look up the first Vcn to move in the first Mcb. If this
+ // Mcb consists of one large hole then there is nothing to
+ // move.
+ //
+
+ Vcn = StartingVcn - StartingVcn1;
+ MoreEntries = FsRtlLookupLargeMcbEntry( &Entry1->LargeMcb,
+ Vcn,
+ &Lcn,
+ &Count,
+ NULL,
+ NULL,
+ &Index );
+
+ //
+ // Loop to move entries over.
+ //
+
+ while (MoreEntries) {
+
+ //
+ // If this entry is not a hole, move it.
+ //
+
+ if (Lcn != UNUSED_LCN) {
+
+ ASSERT( (Vcn - (StartingVcn - StartingVcn1)) >= 0 );
+
+ FsRtlAddLargeMcbEntry( &Entry2->LargeMcb,
+ Vcn - (StartingVcn - StartingVcn1),
+ Lcn,
+ Count );
+ }
+
+ Index += 1;
+
+ MoreEntries = FsRtlGetNextLargeMcbEntry( &Entry1->LargeMcb,
+ Index,
+ &Vcn,
+ &Lcn,
+ &Count );
+ }
+
+ //
+ // Now truncate the original Mcb.
+ //
+
+ FsRtlTruncateLargeMcb( &Entry1->LargeMcb, StartingVcn - StartingVcn1 );
+
+ //
+ // Update the range boundaries.
+ //
+
+ Array1->EndingVcn = StartingVcn - 1;
+ Array2->StartingVcn = StartingVcn;
+
+ if (EndingVcn > Array2->EndingVcn) {
+ Array2->EndingVcn = EndingVcn;
+ }
+
+ //
+ // In the unusual case that we moved the entire first range over,
+ // set to eliminate that range by setting the following variables.
+ //
+
+ if (StartingVcn == Array1->StartingVcn) {
+
+ Array2 = Array1;
+ EndingRangeIndex = StartingRangeIndex;
+
+ //
+ // Otherwise make sure we do not eliminate a range below.
+ //
+
+ } else {
+ Array2 = NULL;
+ }
+ }
+
+ //
+ // We may have emptied all of a range, and
+ // have to eliminate him if so.
+ //
+
+ if (Array2 != NULL) {
+
+ ULONG i;
+
+ //
+ // Make sure the entry is unloaded.
+ //
+
+ UnloadEntry( Mcb, EndingRangeIndex );
+
+ //
+ // We will eliminate one array entry.
+ //
+
+ Mcb->NtfsMcbArraySizeInUse -= 1;
+
+ //
+ // Check if we need to move the ending entries up the array
+ // if so then move them forward, and adjust the back pointers.
+ //
+
+ if (EndingRangeIndex < Mcb->NtfsMcbArraySizeInUse) {
+
+ RtlMoveMemory( Array2,
+ Array2 + 1,
+ sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySizeInUse - EndingRangeIndex));
+
+ for (i = EndingRangeIndex;
+ i < Mcb->NtfsMcbArraySizeInUse;
+ i += 1) {
+
+ if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
+ Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
+ }
+ }
+ }
+ }
+
+ try_return( NOTHING );
+ }
+
+ //
+ // For all remaining cases, the indices must be the same.
+ //
+
+ ASSERT( StartingRangeIndex == EndingRangeIndex );
+
+ //
+ // First catch the case of extending the last range
+ //
+
+ if (((StartingRangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse) &&
+ (StartingVcn == Array1->StartingVcn) &&
+ (EndingVcn >= Array1->EndingVcn)) {
+
+ Array1->EndingVcn = EndingVcn;
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Now handle the case of a disjoint overlap, which can only happen in the
+ // last range. (If this test fails our range is included in an existing
+ // range, and we handle that after this case.)
+ //
+
+ if (StartingVcn > Array2->EndingVcn) {
+
+ LONGLONG OldEndingVcn = Array2->EndingVcn;
+
+ //
+ // Has to be the last range.
+ //
+
+ ASSERT( (EndingRangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse );
+
+ //
+ // First extend the last range to include our new range.
+ //
+
+ Array2->EndingVcn = EndingVcn;
+
+ //
+ // We will be adding a new range and inserting or growing the
+ // previous range up to the new range. If the previous range is
+ // *empty* but has an NtfsMcbEntry then we want to unload the entry.
+ // Otherwise we will grow that range to the correct value but
+ // the Mcb won't contain the clusters for the range. We want
+ // to unload that range and update the OldEndingVcn value so
+ // as not to create two empty ranges prior to this.
+ //
+
+ if ((OldEndingVcn == -1) &&
+ (Array2->NtfsMcbEntry != NULL)) {
+
+ UnloadEntry( Mcb, EndingRangeIndex );
+ }
+
+ //
+ // Now create the range the caller specified.
+ //
+
+ NtfsInsertNewRange( Mcb, StartingVcn, EndingRangeIndex, TRUE );
+
+ //
+ // Now, if this range does not abut the previous last range, *and*
+ // the previous range was not *empty*, then we have to define a
+ // range to contain the unloaded space in the middle.
+ //
+
+ if (((OldEndingVcn + 1) < StartingVcn) &&
+ ((OldEndingVcn + 1) != 0)) {
+
+ NtfsInsertNewRange( Mcb, OldEndingVcn + 1, StartingRangeIndex, TRUE );
+ }
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Check if we really need to insert a new range at the ending vcn
+ // we only need to do the work if there is not already one at that vcn
+ // and this is not the last range
+ //
+ // We do the ending range first, because its index will change if we
+ // insert in the starting range.
+ //
+
+ if (Array2->EndingVcn > EndingVcn) {
+
+ NtfsInsertNewRange( Mcb, EndingVcn + 1, EndingRangeIndex, FALSE );
+ Array1 = &Mcb->NtfsMcbArray[StartingRangeIndex];
+ }
+
+ //
+ // Check if we really need to insert a new range at the starting vcn
+ // we only need to do the work if there is not already one at that vcn
+ //
+
+ if (Array1->StartingVcn < StartingVcn) {
+
+ NtfsInsertNewRange( Mcb, StartingVcn, StartingRangeIndex, FALSE );
+
+ //
+ // In the last range, we may still need to extend.
+ //
+
+ if (EndingVcn > Mcb->NtfsMcbArray[StartingRangeIndex + 1].EndingVcn) {
+ ASSERT((StartingRangeIndex + 2) == Mcb->NtfsMcbArraySizeInUse);
+ Mcb->NtfsMcbArray[StartingRangeIndex + 1].EndingVcn = EndingVcn;
+ }
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
+ }
+
+ return;
+}
+
+
+//
+// Local support routines
+//
+
+ULONG
+NtfsMcbLookupArrayIndex (
+ IN PNTFS_MCB Mcb,
+ IN VCN Vcn
+ )
+
+/*++
+
+Routine Description:
+
+ This routines searches the mcb array for an entry that contains
+ the input vcn value
+
+Arguments:
+
+ Mcb - Supplies the Mcb being queried
+
+ Vcn - Supplies the Vcn to lookup
+
+Return Value:
+
+ ULONG - The index of the entry containing the input Vcn value
+
+--*/
+
+{
+ ULONG Index;
+ ULONG MinIndex;
+ ULONG MaxIndex;
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Do a quick binary search for the entry containing the vcn
+ //
+
+ MinIndex = 0;
+ MaxIndex = Mcb->NtfsMcbArraySizeInUse - 1;
+
+ while (TRUE) {
+
+ Index = (MaxIndex + MinIndex) / 2;
+
+ if (Mcb->NtfsMcbArray[Index].StartingVcn > Vcn) {
+
+ MaxIndex = Index - 1;
+
+ } else if ((Mcb->NtfsMcbArray[Index].EndingVcn < Vcn) &&
+ (Index != Mcb->NtfsMcbArraySizeInUse - 1)) {
+
+ MinIndex = Index + 1;
+
+ } else {
+
+ return Index;
+ }
+ }
+}
+
+
+//
+// Local support routines
+//
+
+VOID
+NtfsInsertNewRange (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN ULONG ArrayIndex,
+ IN BOOLEAN MakeNewRangeEmpty
+ )
+
+/*++
+
+ This routine is used to add a new range at the specified vcn and index location
+
+Arguments:
+
+ Mcb - Supplies the Mcb being modified
+
+ StartingVcn - Supplies the vcn for the new range
+
+ ArrayIndex - Supplies the index currently containing the starting vcn
+
+ MakeNewRangeEmpty - TRUE if the caller wants the new range unloaded regardless
+ of the state of the current range
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ PNTFS_MCB_ENTRY Entry;
+ PNTFS_MCB_ENTRY NewEntry;
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Check if we need to grow the array
+ //
+
+ if (Mcb->NtfsMcbArraySizeInUse >= Mcb->NtfsMcbArraySize) {
+
+ PNTFS_MCB_ARRAY NewArray;
+ ULONG OldArraySize = Mcb->NtfsMcbArraySize;
+
+ //
+ // Test for initial case where we only have one array entry.
+ //
+
+ if (Mcb->NtfsMcbArraySize == 1) {
+
+ //
+ // Convince ourselves that we do not have to move the array entry.
+ //
+
+ ASSERT(FIELD_OFFSET(NTFS_MCB_INITIAL_STRUCTS, Phase1.SingleMcbArrayEntry) ==
+ FIELD_OFFSET(NTFS_MCB_INITIAL_STRUCTS, Phase2.ThreeMcbArrayEntries));
+
+ if (Mcb->NtfsMcbArray[0].NtfsMcbEntry != NULL) {
+
+ //
+ // Allocate a new Mcb Entry, copy the current one over and change the pointer.
+ //
+
+ Entry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
+
+ //
+ // Once space is allocated, dequeue the old entry.
+ //
+
+ ExAcquireFastMutex( &NtfsMcbFastMutex );
+ NtfsMcbDequeueLruEntry( Mcb, Mcb->NtfsMcbArray[0].NtfsMcbEntry );
+
+ RtlCopyMemory( Entry, Mcb->NtfsMcbArray[0].NtfsMcbEntry, sizeof(NTFS_MCB_ENTRY) );
+
+ Mcb->NtfsMcbArray[0].NtfsMcbEntry = Entry;
+
+ NtfsMcbEnqueueLruEntry( Mcb, Entry );
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+ }
+
+ //
+ // Now change to using the three array elements
+ //
+
+ Mcb->NtfsMcbArraySize = 3;
+
+ } else {
+
+ //
+ // If we do then allocate an array that can contain 8 more entires
+ //
+
+ NewArray = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySize + 8), 'mftN' );
+ Mcb->NtfsMcbArraySize += 8;
+
+ //
+ // Copy over the memory from the old array to the new array and then
+ // for every loaded entry we need to adjust its back pointer to the
+ // array
+ //
+
+ RtlCopyMemory( NewArray, Mcb->NtfsMcbArray, sizeof(NTFS_MCB_ARRAY) * Mcb->NtfsMcbArraySizeInUse);
+
+ for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
+
+ if (NewArray[i].NtfsMcbEntry != NULL) {
+
+ NewArray[i].NtfsMcbEntry->NtfsMcbArray = &NewArray[i];
+ }
+ }
+
+ //
+ // Free the old array if it was not the original array.
+ //
+
+ if (OldArraySize > 3) {
+ NtfsFreePool( Mcb->NtfsMcbArray );
+ }
+
+ Mcb->NtfsMcbArray = NewArray;
+ }
+
+ //
+ // Zero the new part of the array.
+ //
+
+ ASSERT(sizeof(NTFS_MCB_ARRAY) == ((PCHAR)&NewArray[1] - (PCHAR)&NewArray[0]));
+
+ RtlZeroMemory( &Mcb->NtfsMcbArray[OldArraySize],
+ (Mcb->NtfsMcbArraySize - OldArraySize) * sizeof(NTFS_MCB_ARRAY) );
+ }
+
+ //
+ // Now move entries that are beyond the array index over by one to make
+ // room for the new entry
+ //
+
+ if (ArrayIndex + 2 <= Mcb->NtfsMcbArraySizeInUse) {
+
+ RtlMoveMemory( &Mcb->NtfsMcbArray[ArrayIndex + 2],
+ &Mcb->NtfsMcbArray[ArrayIndex + 1],
+ sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySizeInUse - ArrayIndex - 1));
+
+ for (i = ArrayIndex + 2; i < Mcb->NtfsMcbArraySizeInUse + 1; i += 1) {
+
+ if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
+
+ Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
+ }
+ }
+ }
+
+ //
+ // Increment our in use count by one
+ //
+
+ Mcb->NtfsMcbArraySizeInUse += 1;
+
+ //
+ // Now fix the starting and ending Vcn values for the old entry and the
+ // new entry
+ //
+
+ Mcb->NtfsMcbArray[ArrayIndex + 1].StartingVcn = StartingVcn;
+ Mcb->NtfsMcbArray[ArrayIndex + 1].EndingVcn = Mcb->NtfsMcbArray[ArrayIndex].EndingVcn;
+ Mcb->NtfsMcbArray[ArrayIndex + 1].NtfsMcbEntry = NULL;
+
+ Mcb->NtfsMcbArray[ArrayIndex].EndingVcn = StartingVcn - 1;
+
+ //
+ // Now if the entry is old entry is not null then we have a bunch of work to do
+ //
+
+ if (!MakeNewRangeEmpty && (Entry = Mcb->NtfsMcbArray[ArrayIndex].NtfsMcbEntry) != NULL) {
+
+ LONGLONG Vcn;
+ LONGLONG Lcn;
+ LONGLONG RunLength;
+ ULONG Index;
+ BOOLEAN FreeNewEntry = FALSE;
+
+ //
+ // Use a try-finally in case the Mcb initialization fails.
+ //
+
+ try {
+
+ //
+ // Allocate the new entry slot
+ //
+
+ NewEntry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
+
+ FreeNewEntry = TRUE;
+ NewEntry->NtfsMcb = Mcb;
+ NewEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[ArrayIndex + 1];
+ FsRtlInitializeLargeMcb( &NewEntry->LargeMcb, Mcb->PoolType );
+
+ ExAcquireFastMutex( &NtfsMcbFastMutex );
+ NtfsMcbEnqueueLruEntry( Mcb, NewEntry );
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+
+ //
+ // Now that the initialization is complete we can store
+ // this entry in the Mcb array. This will now be cleaned
+ // up with the Scb if there is a future error.
+ //
+
+ Mcb->NtfsMcbArray[ArrayIndex + 1].NtfsMcbEntry = NewEntry;
+ FreeNewEntry = FALSE;
+
+ //
+ // Lookup the entry containing the starting vcn in the old entry and put it
+ // in the new entry. But only if the entry exists otherwise we know that
+ // the large mcb doesn't extend into the new range
+ //
+
+ if (FsRtlLookupLargeMcbEntry( &Entry->LargeMcb,
+ StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn,
+ &Lcn,
+ &RunLength,
+ NULL,
+ NULL,
+ &Index )) {
+
+ if (Lcn != UNUSED_LCN) {
+
+ FsRtlAddLargeMcbEntry( &NewEntry->LargeMcb,
+ 0,
+ Lcn,
+ RunLength );
+ }
+
+ //
+ // Now for every run in the old entry that is beyond the starting vcn we will
+ // copy it into the new entry. This will also copy over the dummy run at the end
+ // of the mcb if it exists
+ //
+
+ for (i = Index + 1; FsRtlGetNextLargeMcbEntry( &Entry->LargeMcb, i, &Vcn, &Lcn, &RunLength ); i += 1) {
+
+ if (Lcn != UNUSED_LCN) {
+ ASSERT( (Vcn - (StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn)) >= 0 );
+ FsRtlAddLargeMcbEntry( &NewEntry->LargeMcb,
+ Vcn - (StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn),
+ Lcn,
+ RunLength );
+ }
+ }
+
+ //
+ // Now modify the old mcb to be smaller and put in the dummy run
+ //
+
+ FsRtlTruncateLargeMcb( &Entry->LargeMcb,
+ StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn );
+ }
+
+ } finally {
+
+ if (FreeNewEntry) { NtfsFreePool( NewEntry ); }
+ }
+ }
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return;
+}
+
+
+//
+// Local support routines
+//
+
+VOID
+NtfsCollapseRanges (
+ IN PNTFS_MCB Mcb,
+ IN ULONG StartingArrayIndex,
+ IN ULONG EndingArrayIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will remove the indicated array entries
+
+Arguments:
+
+ Mcb - Supplies the Mcb being modified
+
+ StartingArrayIndex - Supplies the first index to remove
+
+ EndingArrayIndex - Supplies the last index to remove
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // Make sure all the ranges are unloaded.
+ //
+
+ DebugDoit(
+
+ for (i = StartingArrayIndex; i <= EndingArrayIndex; i++) {
+ ASSERT(Mcb->NtfsMcbArray[i].NtfsMcbEntry == NULL);
+ }
+ );
+
+ //
+ // We keep the first entry by we need to copy over
+ // the ending vcn of the last entry
+ //
+
+ Mcb->NtfsMcbArray[StartingArrayIndex].EndingVcn = Mcb->NtfsMcbArray[EndingArrayIndex].EndingVcn;
+
+ //
+ // Check if we need to move the ending entries up the array
+ // if so then move them forward, and adjust the back pointers.
+ //
+
+ if (EndingArrayIndex < Mcb->NtfsMcbArraySizeInUse - 1) {
+
+ RtlMoveMemory( &Mcb->NtfsMcbArray[StartingArrayIndex + 1],
+ &Mcb->NtfsMcbArray[EndingArrayIndex + 1],
+ sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySizeInUse - EndingArrayIndex - 1));
+
+ for (i = StartingArrayIndex + 1;
+ i <= (StartingArrayIndex + Mcb->NtfsMcbArraySizeInUse - EndingArrayIndex - 1);
+ i += 1) {
+
+ if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
+
+ Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
+ }
+ }
+ }
+
+ //
+ // Decrement the in use count and return to our caller
+ //
+
+ Mcb->NtfsMcbArraySizeInUse -= (EndingArrayIndex - StartingArrayIndex);
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+NtfsMcbCleanupLruQueue (
+ IN PVOID Parameter
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called as an ex work queue item and its job is
+ to free up the lru queue until we reach the low water mark
+
+
+Arguments:
+
+ Parameter - ignored
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY Links;
+
+ PNTFS_MCB Mcb;
+ PNTFS_MCB_ARRAY Array;
+ PNTFS_MCB_ENTRY Entry;
+
+ UNREFERENCED_PARAMETER( Parameter );
+
+ //
+ // Grab the global lock
+ //
+
+ ExAcquireFastMutex( &NtfsMcbFastMutex );
+
+ try {
+
+ //
+ // Scan through the lru queue until we either exhaust the queue
+ // or we've trimmed enough
+ //
+
+ for (Links = NtfsMcbLruQueue.Flink;
+ (Links != &NtfsMcbLruQueue) && (NtfsMcbCurrentLevel > NtfsMcbLowWaterMark);
+ Links = Links->Flink ) {
+
+ //
+ // Get the entry and the mcb it points to
+ //
+
+ Entry = CONTAINING_RECORD( Links, NTFS_MCB_ENTRY, LruLinks );
+
+ Mcb = Entry->NtfsMcb;
+
+ //
+ // Skip this entry if it is in the open attribute table.
+ //
+
+ if (((PSCB)(Mcb->FcbHeader))->NonpagedScb->OpenAttributeTableIndex != 0) {
+
+ continue;
+ }
+
+ //
+ // Try and lock the mcb
+ //
+
+ if (NtfsLockNtfsMcb( Mcb )) {
+
+ NtfsVerifyNtfsMcb(Mcb);
+
+ //
+ // The previous test was an unsafe test. Check again in case
+ // this entry has been added.
+ //
+
+ if (((PSCB)(Mcb->FcbHeader))->NonpagedScb->OpenAttributeTableIndex == 0) {
+
+ //
+ // We locked the mcb so we can remove this entry, but
+ // first backup our links pointer so we can continue with the loop
+ //
+
+ Links = Links->Blink;
+
+ //
+ // Get a point to the array entry and then remove this entry and return
+ // it to pool
+ //
+
+ Array = Entry->NtfsMcbArray;
+
+ Array->NtfsMcbEntry = NULL;
+ NtfsMcbDequeueLruEntry( Mcb, Entry );
+ FsRtlUninitializeLargeMcb( &Entry->LargeMcb );
+ if (Mcb->NtfsMcbArraySize != 1) {
+ NtfsFreePool( Entry );
+ }
+ }
+
+ NtfsUnlockNtfsMcb( Mcb );
+ }
+ }
+
+ } finally {
+
+ //
+ // Say we're done with the cleanup so that another one can be fired off when
+ // necessary
+ //
+
+ NtfsMcbCleanupInProgress = FALSE;
+
+ ExReleaseFastMutex( &NtfsMcbFastMutex );
+ }
+
+ //
+ // Return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+BOOLEAN
+NtfsLockNtfsMcb (
+ IN PNTFS_MCB Mcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine attempts to get the Fcb resource(s) exclusive so that
+ ranges may be unloaded.
+
+Arguments:
+
+ Mcb - Supplies the mcb being queried
+
+Return Value:
+
+--*/
+
+{
+ //
+ // Try to acquire paging resource exclusive.
+ //
+
+ if ((Mcb->FcbHeader->PagingIoResource == NULL) ||
+ ExAcquireResourceExclusive(Mcb->FcbHeader->PagingIoResource, FALSE)) {
+
+ //
+ // Now we can try to acquire the main resource exclusively as well.
+ //
+
+ if (ExAcquireResourceExclusive(Mcb->FcbHeader->Resource, FALSE)) {
+ return TRUE;
+ }
+
+ //
+ // We failed to acquire the paging I/O resource, so free the main one
+ // on the way out.
+ //
+
+ if (Mcb->FcbHeader->PagingIoResource != NULL) {
+ ExReleaseResource( Mcb->FcbHeader->PagingIoResource );
+ }
+ }
+
+ //
+ // Could not get this file exclusive.
+ //
+
+ return FALSE;
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+NtfsUnlockNtfsMcb (
+ IN PNTFS_MCB Mcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an mcb is properly formed
+
+Arguments:
+
+ Mcb - Supplies the mcb being queried
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // If there is a paging I/O resource, release it first.
+ //
+
+ if (Mcb->FcbHeader->PagingIoResource != NULL) {
+ ExReleaseResource(Mcb->FcbHeader->PagingIoResource);
+ }
+
+ //
+ // Now release the main resource.
+ //
+
+ ExReleaseResource(Mcb->FcbHeader->Resource);
+}
+
+#ifdef NTFS_VERIFY_MCB
+
+//
+// Local support routine
+//
+
+VOID
+NtfsVerifyNtfsMcb (
+ IN PNTFS_MCB Mcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an mcb is properly formed
+
+Arguments:
+
+ Mcb - Supplies the mcb being queried
+
+Return Value:
+
+--*/
+
+{
+ ULONG i;
+ PNTFS_MCB_ARRAY Array;
+ PNTFS_MCB_ENTRY Entry;
+
+ LONGLONG Vbn;
+ LONGLONG Lbn;
+
+ ASSERT(Mcb->FcbHeader != NULL);
+ ASSERT(Mcb->FcbHeader->NodeTypeCode != 0);
+
+ ASSERT((Mcb->PoolType == PagedPool) || (Mcb->PoolType == NonPagedPool));
+
+ ASSERT(Mcb->NtfsMcbArraySizeInUse <= Mcb->NtfsMcbArraySize);
+
+ for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
+
+ Array = &Mcb->NtfsMcbArray[i];
+
+ ASSERT(((i == 0) && (Array->StartingVcn == 0)) ||
+ ((i != 0) && (Array->StartingVcn != 0)));
+
+ ASSERT(Array->StartingVcn <= (Array->EndingVcn + 1));
+
+ if ((Entry = Array->NtfsMcbEntry) != NULL) {
+
+ ASSERT(Entry->NtfsMcb == Mcb);
+ ASSERT(Entry->NtfsMcbArray == Array);
+
+ if (FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, &Vbn, &Lbn )) {
+ ASSERT( Vbn <= (Array->EndingVcn - Array->StartingVcn) );
+ }
+ }
+ }
+}
+
+
+//
+// Local support routine
+//
+
+VOID
+NtfsVerifyUncompressedNtfsMcb (
+ IN PNTFS_MCB Mcb,
+ IN LONGLONG StartingVcn,
+ IN LONGLONG EndingVcn
+ )
+
+/*++
+
+Routine Description:
+
+ This routines checks if an mcb is for an uncompressed scb and then
+ checks that there are no holes in the mcb. Holes within the range being
+ removed are legal provided EndingVcn is max long long.
+
+Arguments:
+
+ Mcb - Supplies the Mcb being examined
+
+ StartingVcn - The starting Vcn being unloaded
+
+ EndingVcn - The ending Vcn being unloaded
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG i;
+ ULONG j;
+ PNTFS_MCB_ARRAY Array;
+ PNTFS_MCB_ENTRY Entry;
+
+ LONGLONG Vbn;
+ LONGLONG Lbn;
+ LONGLONG Count;
+
+ //
+ // Check if the scb is compressed
+ //
+
+ if (((PSCB)Mcb->FcbHeader)->CompressionUnit != 0) { return; }
+
+ //
+ // For each large mcb in the ntfs mcb we will make sure it doesn't
+ // have any holes.
+ //
+
+ for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
+
+ Array = &Mcb->NtfsMcbArray[i];
+
+ if ((Entry = Array->NtfsMcbEntry) != NULL) {
+
+ for (j = 0; FsRtlGetNextLargeMcbEntry(&Entry->LargeMcb,j,&Vbn,&Lbn,&Count); j += 1) {
+
+ ASSERT((Lbn != -1) ||
+ ((Vbn + Array->StartingVcn >= StartingVcn) && (EndingVcn == MAXLONGLONG)) ||
+ FlagOn(((PSCB)Mcb->FcbHeader)->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS));
+ }
+ }
+ }
+
+ return;
+}
+#endif