/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    hivebin.c

Abstract:

    This module implements HvpAddBin - used to grow a hive.

--*/

#include    "cmp.h"

//
// Private function prototypes
//
BOOLEAN
HvpCoalesceDiscardedBins(
    IN PHHIVE Hive,
    IN ULONG NeededSize,
    IN HSTORAGE_TYPE Type
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HvpAddBin)
#pragma alloc_text(PAGE,HvpCoalesceDiscardedBins)
#endif


PHBIN
HvpAddBin(
    IN PHHIVE  Hive,
    IN ULONG   NewSize,
    IN HSTORAGE_TYPE   Type
    )
/*++

Routine Description:

    Grows either the Stable or Volatile storage of a hive by adding
    a new bin.  Bin will be allocated space in Stable store (e.g. file)
    if Type == Stable.  Memory image will be allocated and initialized.
    Map will be grown and filled in to describe the new bin.
    
      
WARNING:
    When adding a new bin, if the CM_VIEW_SIZE boundary is crossed:
    - add a free bin with the remaining space to the first CM_VIEW_SIZE barrier
    - from the next CM_VIEW_SIZE window, add a new bin of the desired size.

    Of course, this applies only to stable storage.

Arguments:

    Hive - supplies a pointer to the hive control structure for the
            hive of interest

    NewSize - size of the object caller wishes to put in the hive.  New
                bin will be at least large enough to hold this.

    Type - Stable or Volatile

Return Value:

    Pointer to the new BIN if we succeeded, NULL if we failed.

--*/
{
    BOOLEAN         UseForIo;
    PHBIN           NewBin;
    PHBIN           RemainingBin;
    ULONG           OldLength;
    ULONG           NewLength;
    ULONG           CheckLength;
    ULONG           OldMap;
    ULONG           NewMap;
    ULONG           OldTable;
    ULONG           NewTable;
    PHMAP_DIRECTORY Dir = NULL;
    PHMAP_TABLE     newt = NULL;
    PHMAP_ENTRY     Me;
    PHCELL          t;
    ULONG           i;
    ULONG           j;
    PULONG          NewVector = NULL;
    PLIST_ENTRY     Entry;
    PFREE_HBIN      FreeBin = NULL;
    ULONG           TotalDiscardedSize;
    PCMHIVE			CmHive;

    CM_PAGED_CODE();

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_HIVE,"HvpAddBin:\n"));
    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_HIVE,"\tHive=%p NewSize=%08lx\n",Hive,NewSize));

    CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);

    ASSERT_HIVE_WRITER_LOCK_OWNED(CmHive);

    // need to hold this, so no flush of partial data occurs
    ASSERT_HIVE_FLUSHER_LOCKED(CmHive);

    RemainingBin = NULL;

    //
    //  Round size up to account for bin overhead.  Caller should
    //  have accounted for cell overhead.
    //
    NewSize += sizeof(HBIN);
    if ((NewSize < HCELL_BIG_ROUND) &&
        ((NewSize % HBLOCK_SIZE) > HBIN_THRESHOLD)) {
        NewSize += HBLOCK_SIZE;
    }

    //
    // Try not to create HBINs smaller than the page size of the machine
    //  (it is not illegal to have bins smaller than the page size, but it
    //  is less efficient)
    //
    NewSize = ROUND_UP(NewSize, ((HBLOCK_SIZE >= PAGE_SIZE) ? HBLOCK_SIZE : PAGE_SIZE));

    //
    // see if there's a discarded HBIN of the right size
    //
    TotalDiscardedSize = 0;

Retry:

    Entry = Hive->Storage[Type].FreeBins.Flink;
    while (Entry != &Hive->Storage[Type].FreeBins) {
        FreeBin = CONTAINING_RECORD(Entry,
                                    FREE_HBIN,
                                    ListEntry);
        TotalDiscardedSize += FreeBin->Size;
        if ((FreeBin->Size >= NewSize) && ((CmHive->GrowOnlyMode == FALSE) || (Type == Volatile)) ) {

            if (!HvMarkDirty(Hive,
                             FreeBin->FileOffset + (Type * HCELL_TYPE_MASK),
                             FreeBin->Size,TRUE)) {
                goto ErrorExit1;
            }
            NewSize = FreeBin->Size;
            ASSERT_LISTENTRY(&FreeBin->ListEntry);
            RemoveEntryList(&FreeBin->ListEntry);

            if ( FreeBin->Flags & FREE_HBIN_DISCARDABLE ) {
                //
                // HBIN is still in memory, don't need any more allocs, just
                // fill in the block addresses.
                //
                Me = NULL;
                for (i=0;i<NewSize;i+=HBLOCK_SIZE) {
                    Me = HvpGetCellMap(Hive, FreeBin->FileOffset+i+(Type*HCELL_TYPE_MASK));
                    VALIDATE_CELL_MAP(__LINE__,Me,Hive,FreeBin->FileOffset+i+(Type*HCELL_TYPE_MASK));
                    Me->BlockAddress = HBIN_BASE(Me->BinAddress)+i;
                    Me->BinAddress &= ~HMAP_DISCARDABLE;
                    // we cannot have the FREE_BIN_DISCARDABLE flag set 
                    // and FREE_HBIN_INVIEW not set on a mapped bin.
                    ASSERT( Me->BinAddress & HMAP_INPAGEDPOOL );
                    // we don't need to set it to NULL - just for debug purposes
                    ASSERT( (Me->CmView = NULL) == NULL );
                }
                (Hive->Free)(FreeBin, sizeof(FREE_HBIN));
#if DBG 
                {
                    UNICODE_STRING  HiveName;
                    RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
                    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvpAddBin for (%p) (%.*S) reusing FreeBin %p at FileOffset %lx; Type = %lu\n",
                        Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,HBIN_BASE(Me->BinAddress),((PHBIN)HBIN_BASE(Me->BinAddress))->FileOffset,(ULONG)Type));
                }
#endif
                return (PHBIN)HBIN_BASE(Me->BinAddress);
            }
            break;
        }
        Entry = Entry->Flink;
    }

    if ((Entry == &Hive->Storage[Type].FreeBins) &&
        (TotalDiscardedSize >= NewSize)) {
        //
        // No sufficiently large discarded bin was found,
        // but the total discarded space is large enough.
        // Attempt to coalesce adjacent discarded bins into
        // a larger bin and retry.
        //
        if (HvpCoalesceDiscardedBins(Hive, NewSize, Type)) {
            goto Retry;
        }
    }

    //
    // we need these sooner to do the computations in case we allocate a new bin
    //
    OldLength = Hive->Storage[Type].Length;
    CheckLength = OldLength;
    //
    //  Attempt to allocate the bin.
    //
    UseForIo = (BOOLEAN)((Type == Stable) ? TRUE : FALSE);
    if (Entry != &Hive->Storage[Type].FreeBins) {
        if( Type == Volatile ) {
            //
            // old plain method for volatile storage
            //
            //
            // Note we use ExAllocatePool directly here to avoid
            // charging quota for this bin again. When a bin
            // is discarded, its quota is not returned. This prevents
            // sparse hives from requiring more quota after
            // a reboot than on a running system.
            //
            NewBin = ExAllocatePoolWithTag((UseForIo) ? PagedPoolCacheAligned : PagedPool,
                                           NewSize,
                                           CM_HVBIN_TAG);
            if (NewBin == NULL) {
                InsertHeadList(&Hive->Storage[Type].FreeBins, Entry);
                goto ErrorExit1;
            }
            //
            // make sure we don't leak whatever is in the kernel pool out of the box (roaming).
            //
            RtlZeroMemory(NewBin,NewSize);
        } else {
            //
            // for Stable, map the view containing the bin in memory
            // and fix the map
            //

            Me = HvpGetCellMap(Hive, FreeBin->FileOffset);
            VALIDATE_CELL_MAP(__LINE__,Me,Hive,FreeBin->FileOffset);

    
            if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
                ASSERT( (Me->BinAddress & HMAP_INVIEW) == 0 );
                //
                // bin is in paged pool; allocate backing store
                //
                NewBin = (Hive->Allocate)(NewSize, UseForIo,CM_FIND_LEAK_TAG15);
                if (NewBin == NULL) {
                    InsertHeadList(&Hive->Storage[Type].FreeBins, Entry);
                    goto ErrorExit1;
                }
                //
                // make sure we don't leak whatever is in the kernel pool out of the box (roaming).
                //
                RtlZeroMemory(NewBin,NewSize);
            } else {
                //
                // The view containing this bin has been unmapped; map it again
                //
                if( (Me->BinAddress & HMAP_INVIEW) == 0 ) {
                    ASSERT( (Me->BinAddress & HMAP_INPAGEDPOOL) == 0 );
                    //
                    // map the bin
                    //
                    if( !NT_SUCCESS(CmpMapThisBin((PCMHIVE)Hive,FreeBin->FileOffset,TRUE)) ) {
                        InsertHeadList(&Hive->Storage[Type].FreeBins, Entry);
                        return NULL;
                    }
                }

                ASSERT( Me->BinAddress & HMAP_INVIEW );
                NewBin = (PHBIN)HBIN_BASE(Me->BinAddress);
            }
        }
       
    } else {

        //
        // this is a totally new bin. Allocate it from paged pool
        //
        NewBin = (Hive->Allocate)(NewSize, UseForIo,CM_FIND_LEAK_TAG16);
        if (NewBin == NULL) {
            goto ErrorExit1;
        }
        //
        // make sure we don't leak whatever is in the kernel pool out of the box (roaming).
        //
        RtlZeroMemory(NewBin,NewSize);
    }

    //
    // Init the bin
    //
    NewBin->Signature = HBIN_SIGNATURE;
    NewBin->Size = NewSize;
    NewBin->Spare = 0;

    t = (PHCELL)((PUCHAR)NewBin + sizeof(HBIN));
    t->Size = NewSize - sizeof(HBIN);
    if (USE_OLD_CELL(Hive)) {
        t->u.OldCell.Last = (ULONG)HBIN_NIL;
    }

    if (Entry != &Hive->Storage[Type].FreeBins) {
        //
        // found a discarded HBIN we can use, just fill in the map and we
        // are done.
        //
        for (i=0;i<NewSize;i+=HBLOCK_SIZE) {
            Me = HvpGetCellMap(Hive, FreeBin->FileOffset+i+(Type*HCELL_TYPE_MASK));
            VALIDATE_CELL_MAP(__LINE__,Me,Hive,FreeBin->FileOffset+i+(Type*HCELL_TYPE_MASK));
            Me->BlockAddress = (ULONG_PTR)NewBin + i;
            //
            //  make sure to preserve the following flags:
            // HMAP_INVIEW|HMAP_INPAGEDPOOL
            //  and to clear the flag
            // HMAP_DISCARDABLE
            //
                        
            Me->BinAddress = (ULONG_PTR)((ULONG_PTR)NewBin | (Me->BinAddress&(HMAP_INVIEW|HMAP_INPAGEDPOOL)));
            Me->BinAddress &= ~HMAP_DISCARDABLE;
            if (i==0) {
                Me->BinAddress |= HMAP_NEWALLOC;
                Me->MemAlloc = NewSize;
            } else {
                Me->MemAlloc = 0;
            }

        }

        NewBin->FileOffset = FreeBin->FileOffset;

        (Hive->Free)(FreeBin, sizeof(FREE_HBIN));

#if DBG
        {
            UNICODE_STRING  HiveName;
            RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
            CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvpAddBin for (%p) (%.*S) reusing FreeBin %p at FileOffset %lx; Type = %lu\n",
                Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,NewBin,NewBin->FileOffset,(ULONG)Type));
        }
#endif

        return(NewBin);
    }


    //
    // Compute map growth needed, grow the map
    //

    if( (HvpCheckViewBoundary(CheckLength,CheckLength + NewSize - 1) == FALSE) &&
        (NewSize < CM_VIEW_SIZE)    // don't bother if we attempt to allocate a cell bigger then the view size
                                    // it'll cross the boundary anyway.
        ) {
        //
        // the bin to be allocated doesn't fit into the remaining 
        // of this CM_VIEW_SIZE window. Allocate it from the next CM_VIEW_SIZE window
        // and add the remaining of this to the free bin list
        //
        CheckLength += (NewSize+HBLOCK_SIZE);
        CheckLength &= (~(CM_VIEW_SIZE - 1));
        CheckLength -= HBLOCK_SIZE;
        
#if DBG
        {
            UNICODE_STRING  HiveName;
            RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
            CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvpAddBin for (%p) (%.*S) crossing boundary at %lx Size %lx, newoffset= %lx\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,OldLength,NewSize,CheckLength));
        }
#endif
    }

    NewLength = CheckLength + NewSize;
    NewBin->FileOffset = CheckLength;

    if( CmpCanGrowSystemHive(Hive,NewLength) == FALSE ) {
        //
        // we have reached the hard quota limit on the system hive
        //
        goto ErrorExit2;
    }

    ASSERT((OldLength % HBLOCK_SIZE) == 0);
    ASSERT((CheckLength % HBLOCK_SIZE) == 0);
    ASSERT((NewLength % HBLOCK_SIZE) == 0);

    if (OldLength == 0) {
        //
        // Need to create the first table
        //
        newt = (PVOID)((Hive->Allocate)(sizeof(HMAP_TABLE), FALSE,CM_FIND_LEAK_TAG17));
        if (newt == NULL) {
            goto ErrorExit2;
        }
        RtlZeroMemory(newt, sizeof(HMAP_TABLE));
        Hive->Storage[Type].SmallDir = newt;
        Hive->Storage[Type].Map = (PHMAP_DIRECTORY)&(Hive->Storage[Type].SmallDir);
    }

    if (OldLength > 0) {
        OldMap = (OldLength-1) / HBLOCK_SIZE;
    } else {
        OldMap = 0;
    }
    NewMap = (NewLength-1) / HBLOCK_SIZE;

    OldTable = OldMap / HTABLE_SLOTS;
    NewTable = NewMap / HTABLE_SLOTS;

#if DBG
    if( Type == Stable ) {
        UNICODE_STRING  HiveName;
        RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
        CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvpAddBin for (%p) (%.*S) Adding new bin %p at FileOffset %lx; Type = %lu\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,NewBin,NewBin->FileOffset,(ULONG)Type));
    }
#endif

    if (NewTable != OldTable) {

        //
        // Need some new Tables
        //
        if (OldTable == 0) {

            //
            // We can get here even if the real directory has already been created.
            // This can happen if we create the directory then fail on something 
            // later. So we need to handle the case where a directory already exists.
            //
            if (Hive->Storage[Type].Map == (PHMAP_DIRECTORY)&Hive->Storage[Type].SmallDir) {
                ASSERT(Hive->Storage[Type].SmallDir != NULL);

                //
                // Need a real directory
                //
                Dir = (Hive->Allocate)(sizeof(HMAP_DIRECTORY), FALSE,CM_FIND_LEAK_TAG18);
                if (Dir == NULL) {
                    goto ErrorExit2;
                }
                RtlZeroMemory(Dir, sizeof(HMAP_DIRECTORY));
    
                Dir->Directory[0] = Hive->Storage[Type].SmallDir;
                Hive->Storage[Type].SmallDir = NULL;
    
                Hive->Storage[Type].Map = Dir;
            } else {
                ASSERT(Hive->Storage[Type].SmallDir == NULL);
            }

        }
        Dir = Hive->Storage[Type].Map;

        //
        // Fill in directory with new tables
        //
        if (HvpAllocateMap(Hive, Dir, OldTable+1, NewTable) ==  FALSE) {
            goto ErrorExit3;
        }
    }

    //
    // If Type == Stable, and the hive is not marked WholeHiveVolatile,
    // grow the file, the log, and the DirtyVector
    //
    if( !NT_SUCCESS(HvpAdjustHiveFreeDisplay(Hive,NewLength,Type)) ) {
        goto ErrorExit3;
    }

    Hive->Storage[Type].Length = NewLength;
    if ((Type == Stable) && (!(Hive->HiveFlags & HIVE_VOLATILE))) {

        //
        // Grow the dirtyvector
        //
        NewVector = (PULONG)(Hive->Allocate)(ROUND_UP(NewMap+1,sizeof(ULONG)), TRUE,CM_FIND_LEAK_TAG19);
        if (NewVector == NULL) {
            goto ErrorExit3;
        }

        RtlZeroMemory(NewVector, NewMap+1);

        if (Hive->DirtyVector.Buffer != NULL) {

            RtlCopyMemory(
                (PVOID)NewVector,
                (PVOID)Hive->DirtyVector.Buffer,
                OldMap+1
                );
            (Hive->Free)(Hive->DirtyVector.Buffer, Hive->DirtyAlloc);
        }

        RtlInitializeBitMap(
            &(Hive->DirtyVector),
            NewVector,
            NewLength / HSECTOR_SIZE
            );
        Hive->DirtyAlloc = ROUND_UP(NewMap+1,sizeof(ULONG));

        //
        // Grow the log
        //
        if ( ! (HvpGrowLog2(Hive, NewSize))) {
            goto ErrorExit4;
        }

        //
        // Grow the primary
        //
        if ( !  (Hive->FileSetSize)(
                    Hive,
                    HFILE_TYPE_PRIMARY,
                    NewLength+HBLOCK_SIZE,
                    OldLength+HBLOCK_SIZE
                    ) )
        {
            goto ErrorExit4;
        }

        //
        // Mark new bin dirty so all control structures get written at next sync
        //
        ASSERT( ((NewLength - OldLength) % HBLOCK_SIZE) == 0 );
        if ( ! HvMarkDirty(Hive, OldLength,NewLength - OldLength,FALSE)) {
            //
            // we have grown the hive, so the new bins are in paged pool !!!
            //
            goto ErrorExit4;
        }
    } 

    //
    // Add the remaining to the free bin list
    //
    if( CheckLength != OldLength ) {
        //
        // Allocate the bin from pagedpool (first flush will update the file image and free the memory)
        //
        RemainingBin = (Hive->Allocate)(CheckLength - OldLength, UseForIo,CM_FIND_LEAK_TAG20);
        if (RemainingBin == NULL) {
            goto ErrorExit4;
        }
        //
        // make sure we don't leak whatever is in the kernel pool out of the box (roaming).
        //
        RtlZeroMemory(RemainingBin,CheckLength - OldLength);

        RemainingBin->Signature = HBIN_SIGNATURE;
        RemainingBin->Size = CheckLength - OldLength;
        RemainingBin->FileOffset = OldLength;
        RemainingBin->Spare = 0;

        t = (PHCELL)((PUCHAR)RemainingBin + sizeof(HBIN));
        t->Size = RemainingBin->Size - sizeof(HBIN);
        if (USE_OLD_CELL(Hive)) {
            t->u.OldCell.Last = (ULONG)HBIN_NIL;
        }

        //
        // add the free bin to the free bin list and update the map.
        //
        FreeBin = (Hive->Allocate)(sizeof(FREE_HBIN), FALSE,CM_FIND_LEAK_TAG21);
        if (FreeBin == NULL) {
            goto ErrorExit5;
        }
        
        FreeBin->Size = CheckLength - OldLength;
        FreeBin->FileOffset = OldLength;
        FreeBin->Flags = FREE_HBIN_DISCARDABLE;

        InsertHeadList(&Hive->Storage[Type].FreeBins, &FreeBin->ListEntry);
        
        ASSERT_LISTENTRY(&FreeBin->ListEntry);
        ASSERT_LISTENTRY(FreeBin->ListEntry.Flink);

        for (i = OldLength; i < CheckLength; i += HBLOCK_SIZE) {
            Me = HvpGetCellMap(Hive, i + (Type*HCELL_TYPE_MASK));
            VALIDATE_CELL_MAP(__LINE__,Me,Hive,i + (Type*HCELL_TYPE_MASK));

            Me->BinAddress = (ULONG_PTR)RemainingBin | HMAP_DISCARDABLE | HMAP_INPAGEDPOOL;
            if( i == OldLength ) {
                Me->BinAddress |= HMAP_NEWALLOC;
                Me->MemAlloc = CheckLength - OldLength;
            } else {
                Me->MemAlloc = 0;
            }
            Me->BlockAddress = (ULONG_PTR)FreeBin;

            // we don't need to set it to NULL - just for debug purposes
            ASSERT( (Me->CmView = NULL) == NULL );
        }

#if DBG
        {
            if( Type == Stable ) {
                UNICODE_STRING  HiveName;
                RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
                CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvpAddBin for (%p) (%.*S) adding bin starting at %lx size %lx to FreeBinList\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,FreeBin->FileOffset,FreeBin->Size));
            }
        }
#endif
    }
    //
    // Fill in the map, mark new allocation.
    //
    j = 0;
    for (i = CheckLength; i < NewLength; i += HBLOCK_SIZE) {
        Me = HvpGetCellMap(Hive, i + (Type*HCELL_TYPE_MASK));
        VALIDATE_CELL_MAP(__LINE__,Me,Hive,i + (Type*HCELL_TYPE_MASK));
        Me->BlockAddress = (ULONG_PTR)NewBin + j;
        Me->BinAddress = (ULONG_PTR)NewBin;
        Me->BinAddress |= HMAP_INPAGEDPOOL;
        // we don't need to set it to NULL - just for debug purposes
        ASSERT( (Me->CmView = NULL) == NULL );

        if (j == 0) {
            //
            // First block of allocation, mark it.
            //
            Me->BinAddress |= HMAP_NEWALLOC;
            Me->MemAlloc = NewSize;
        } else {
            Me->MemAlloc = 0;
        }
        j += HBLOCK_SIZE;
    }

    if( Type == Stable) {
        CmpUpdateSystemHiveHysteresis(Hive,NewLength,OldLength);
    }
    return NewBin;

ErrorExit5:
    if( RemainingBin != NULL ){
        (Hive->Free)(RemainingBin, RemainingBin->Size);
    }
ErrorExit4:
    if((Type == Stable) && (!(Hive->HiveFlags & HIVE_VOLATILE))) {
        RtlInitializeBitMap(&Hive->DirtyVector,
                            NewVector,
                            OldLength / HSECTOR_SIZE);
        Hive->DirtyCount = RtlNumberOfSetBits(&Hive->DirtyVector);
    }
ErrorExit3:
    Hive->Storage[Type].Length = OldLength;
    HvpFreeMap(Hive, Dir, OldTable+1, NewTable);

ErrorExit2:
    (Hive->Free)(NewBin, NewSize);
    if( newt != NULL ) {
        (Hive->Free)(newt,sizeof(HMAP_TABLE));    
    }

ErrorExit1:
    return NULL;
}

BOOLEAN
HvpCoalesceDiscardedBins(
    IN PHHIVE Hive,
    IN ULONG NeededSize,
    IN HSTORAGE_TYPE Type
    )

/*++

Routine Description:

    Walks through the list of discarded bins and attempts to
    coalesce adjacent discarded bins into one larger bin in
    order to satisfy an allocation request.

    It doesn't coalesce bins over the CM_VIEW_SIZE boundary.

    It doesn't coalesce bins from paged pool with bins mapped in
    system cache views.

Arguments:

    Hive - Supplies pointer to hive control block.

    NeededSize - Supplies size of allocation needed.

    Type - Stable or Volatile

Return Value:

    TRUE - A bin of the desired size was created.

    FALSE - No bin of the desired size could be created.

--*/

{
    PLIST_ENTRY List;
    PFREE_HBIN FreeBin;
    PFREE_HBIN PreviousFreeBin;
    PFREE_HBIN NextFreeBin;
    PHMAP_ENTRY Map;
    PHMAP_ENTRY PreviousMap;
    PHMAP_ENTRY NextMap;
    ULONG MapBlock;

    ASSERT_HIVE_WRITER_LOCK_OWNED((PCMHIVE)Hive);

    List = Hive->Storage[Type].FreeBins.Flink;

    while (List != &Hive->Storage[Type].FreeBins) {
        FreeBin = CONTAINING_RECORD(List, FREE_HBIN, ListEntry);

        if ((FreeBin->Flags & FREE_HBIN_DISCARDABLE)==0) {

            Map = HvpGetCellMap(Hive, FreeBin->FileOffset);
            VALIDATE_CELL_MAP(__LINE__,Map,Hive,FreeBin->FileOffset);

            //
            // Scan backwards, coalescing previous discarded bins
            //
            while (FreeBin->FileOffset > 0) {
                PreviousMap = HvpGetCellMap(Hive, FreeBin->FileOffset - HBLOCK_SIZE);
                VALIDATE_CELL_MAP(__LINE__,PreviousMap,Hive,FreeBin->FileOffset - HBLOCK_SIZE);
                if( (BIN_MAP_ALLOCATION_TYPE(Map) != BIN_MAP_ALLOCATION_TYPE(PreviousMap)) || // different allocation type
                    ((PreviousMap->BinAddress & HMAP_DISCARDABLE) == 0) // previous bin is not discardable
                    ){
                    break;
                }
                
                PreviousFreeBin = (PFREE_HBIN)PreviousMap->BlockAddress;

                if (PreviousFreeBin->Flags & FREE_HBIN_DISCARDABLE) {
                    //
                    // this bin has not yet been discarded; can't coalesce with it.
                    //
                    break;
                }
                
                if( HvpCheckViewBoundary(PreviousFreeBin->FileOffset,PreviousFreeBin->FileOffset + PreviousFreeBin->Size + FreeBin->Size - 1) == FALSE ) {
                    //
                    // don't coalesce bins over the CM_VIEW_SIZE boundary
                    //
                    // substract 1 because addresses are from 0 to size - 1 !!!
                    //
                    break;
                }

                
                RemoveEntryList(&PreviousFreeBin->ListEntry);

                //
                // Fill in all the old map entries with the new one.
                //
                for (MapBlock = 0; MapBlock < PreviousFreeBin->Size; MapBlock += HBLOCK_SIZE) {
                    PreviousMap = HvpGetCellMap(Hive, PreviousFreeBin->FileOffset + MapBlock);
                    VALIDATE_CELL_MAP(__LINE__,PreviousMap,Hive,PreviousFreeBin->FileOffset + MapBlock);
                    PreviousMap->BlockAddress = (ULONG_PTR)FreeBin;
                }

                FreeBin->FileOffset = PreviousFreeBin->FileOffset;
                FreeBin->Size += PreviousFreeBin->Size;
                (Hive->Free)(PreviousFreeBin, sizeof(FREE_HBIN));
            }

            //
            // Scan forwards, coalescing subsequent discarded bins
            //
            while ((FreeBin->FileOffset + FreeBin->Size) < Hive->Storage[Type].Length) {
                NextMap = HvpGetCellMap(Hive, FreeBin->FileOffset + FreeBin->Size);
                VALIDATE_CELL_MAP(__LINE__,NextMap,Hive,FreeBin->FileOffset + FreeBin->Size);
                if( (BIN_MAP_ALLOCATION_TYPE(Map) != BIN_MAP_ALLOCATION_TYPE(NextMap)) || // different allocation type
                    ((NextMap->BinAddress & HMAP_DISCARDABLE) == 0) // previous bin is not discardable
                    ){
                    break;
                }
                NextFreeBin = (PFREE_HBIN)NextMap->BlockAddress;

                if (NextFreeBin->Flags & FREE_HBIN_DISCARDABLE) {
                    //
                    // this bin has not yet been discarded; can't coalesce with it.
                    //
                    break;
                }

                if( HvpCheckViewBoundary(FreeBin->FileOffset,FreeBin->FileOffset + FreeBin->Size + NextFreeBin->Size - 1) == FALSE ) {
                    //
                    // don't coalesce bins over the CM_VIEW_SIZE boundary
                    //
                    // substract 1 because addresses are from 0 to size - 1 !!!
                    //
                    break;
                }
                RemoveEntryList(&NextFreeBin->ListEntry);

                //
                // Fill in all the old map entries with the new one.
                //
                for (MapBlock = 0; MapBlock < NextFreeBin->Size; MapBlock += HBLOCK_SIZE) {
                    NextMap = HvpGetCellMap(Hive, NextFreeBin->FileOffset + MapBlock);
                    VALIDATE_CELL_MAP(__LINE__,NextMap,Hive,NextFreeBin->FileOffset + MapBlock);
                    NextMap->BlockAddress = (ULONG_PTR)FreeBin;
                }

                FreeBin->Size += NextFreeBin->Size;
                (Hive->Free)(NextFreeBin, sizeof(FREE_HBIN));
            }
            if (FreeBin->Size >= NeededSize) {
                return(TRUE);
            }
        }
        List=List->Flink;
    }
    return(FALSE);
}
