Example #1
0
/*
 * @implemented
 */
BOOLEAN
NTAPI
FsRtlFastCheckLockForRead(IN PFILE_LOCK FileLock,
                          IN PLARGE_INTEGER FileOffset,
                          IN PLARGE_INTEGER Length,
                          IN ULONG Key,
                          IN PFILE_OBJECT FileObject,
                          IN PVOID Process)
{
    PEPROCESS EProcess = Process;
    COMBINED_LOCK_ELEMENT ToFind;
    PCOMBINED_LOCK_ELEMENT Found;
    DPRINT("FsRtlFastCheckLockForRead(%wZ, Offset %08x%08x, Length %08x%08x, Key %x)\n", 
           &FileObject->FileName, 
           FileOffset->HighPart,
           FileOffset->LowPart, 
           Length->HighPart,
           Length->LowPart,
           Key);
    ToFind.Exclusive.FileLock.StartingByte = *FileOffset;
    ToFind.Exclusive.FileLock.EndingByte.QuadPart = 
        FileOffset->QuadPart + Length->QuadPart;
    if (!FileLock->LockInformation) return TRUE;
    Found = RtlLookupElementGenericTable
        (FileLock->LockInformation,
         &ToFind);
    if (!Found || !Found->Exclusive.FileLock.ExclusiveLock) return TRUE;
    return Found->Exclusive.FileLock.Key == Key && 
        Found->Exclusive.FileLock.ProcessId == EProcess->UniqueProcessId;
}
PXIXFS_FCB
xixfs_FCBTLBLookupEntry(
	PXIXFS_VCB	VCB,
	uint64		FileId
)
{
	FCB_TABLE_ENTRY		Entry;
	PFCB_TABLE_ENTRY	Hit = NULL;
	PXIXFS_FCB			pFCB = NULL;
	PAGED_CODE();
	DebugTrace(DEBUG_LEVEL_TRACE, (DEBUG_TARGET_CREATE|DEBUG_TARGET_CLOSE| DEBUG_TARGET_FCB),
		("Enter xixfs_FCBTLBLookupEntry \n" ));  

	Entry.FileId = FileId;
	Hit = (PFCB_TABLE_ENTRY) RtlLookupElementGenericTable( &VCB->FCBTable, &Entry);
	
	if(Hit != NULL){
		pFCB = Hit->pFCB;
	}

	DebugTrace(DEBUG_LEVEL_TRACE, (DEBUG_TARGET_CREATE|DEBUG_TARGET_CLOSE| DEBUG_TARGET_FCB),
		("Exit xixfs_FCBTLBLookupEntry \n" ));  

	return pFCB;
}
Example #3
0
/*
 * @implemented
 */
BOOLEAN
NTAPI
FsRtlCheckLockForWriteAccess(IN PFILE_LOCK FileLock,
                             IN PIRP Irp)
{
    BOOLEAN Result;
    PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
    COMBINED_LOCK_ELEMENT ToFind;
    PCOMBINED_LOCK_ELEMENT Found;
    PEPROCESS Process = Irp->Tail.Overlay.Thread->ThreadsProcess;
    DPRINT("CheckLockForWriteAccess(%wZ, Offset %08x%08x, Length %x)\n", 
           &IoStack->FileObject->FileName,
           IoStack->Parameters.Write.ByteOffset.HighPart,
           IoStack->Parameters.Write.ByteOffset.LowPart,
           IoStack->Parameters.Write.Length);
    if (!FileLock->LockInformation) {
        DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
        return TRUE;
    }
    ToFind.Exclusive.FileLock.StartingByte = IoStack->Parameters.Write.ByteOffset;
    ToFind.Exclusive.FileLock.EndingByte.QuadPart = 
        ToFind.Exclusive.FileLock.StartingByte.QuadPart + 
        IoStack->Parameters.Write.Length;
    Found = RtlLookupElementGenericTable
        (FileLock->LockInformation,
         &ToFind);
    if (!Found) {
        DPRINT("CheckLockForWriteAccess(%wZ) => TRUE\n", &IoStack->FileObject->FileName);
        return TRUE;
    }
    Result = Process->UniqueProcessId == Found->Exclusive.FileLock.ProcessId;
    DPRINT("CheckLockForWriteAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE");
    return Result;
}
Example #4
0
/* This function expands the conflicting range Conflict by removing and reinserting it,
   then adds a shared range of the same size */
PCOMBINED_LOCK_ELEMENT
NTAPI
FsRtlpRebuildSharedLockRange
(PFILE_LOCK FileLock,
 PLOCK_INFORMATION LockInfo,
 PCOMBINED_LOCK_ELEMENT Conflict)
{
    /* Starting at Conflict->StartingByte and going to Conflict->EndingByte
     * capture and expand a shared range from the shared range list.
     * Finish when we've incorporated all overlapping shared regions.
     */
    BOOLEAN InsertedNew = FALSE, RemovedOld;
    COMBINED_LOCK_ELEMENT NewElement = *Conflict;
    PCOMBINED_LOCK_ELEMENT Entry;
    while ((Entry = RtlLookupElementGenericTable
            (FileLock->LockInformation, &NewElement)))
    {
        FsRtlpExpandLockElement(&NewElement, Entry);
        RemovedOld = RtlDeleteElementGenericTable
            (&LockInfo->RangeTable,
             Entry);
        ASSERT(RemovedOld);
    }
    Conflict = RtlInsertElementGenericTable
        (&LockInfo->RangeTable,
         &NewElement,
         sizeof(NewElement),
         &InsertedNew);
    ASSERT(InsertedNew);
    return Conflict;
}
Example #5
0
static void RtlSplayTreeTest()
{
    ULONG i, del;
    PCHAR Ch;
    CHAR Text[] = "the quick_brown!fOx-jUmp3d/0vER+THe^lazy.D@g";
    CHAR NewE[] = "11111111111111111111111111111111110111111111";
    RTL_GENERIC_TABLE Table;
    RtlInitializeGenericTable
        (&Table,
         CompareCharTable,
         AllocRoutine,
         FreeRoutine,
         NULL);
    for (i = 0; Text[i]; i++) {
        BOOLEAN WasNew;
        Ch = (PCHAR)RtlInsertElementGenericTable
            (&Table,
             &Text[i],
             sizeof(Text[i]),
             &WasNew);
        ok(Ch && *Ch == Text[i], "Copy character into node\n");
        ok(WasNew == (NewE[i] == '1'),
           "Character newness didn't match for char %u: '%c'\n",
           i, Text[i]);
    }
    for (Ch = (PCHAR)RtlEnumerateGenericTable(&Table, TRUE), i = 0;
         Ch;
         Ch = (PCHAR)RtlEnumerateGenericTable(&Table, FALSE), i++) {
        ok(strchr(Text, *Ch) != NULL, "Nonexistent character\n");
    }
    ok(RtlNumberGenericTableElements(&Table) == strlen(Text) - 1, "Not the right number of elements\n");
    ok(RtlLookupElementGenericTable(&Table, "q") != NULL, "Could not lookup q\n");
    ok(!RtlLookupElementGenericTable(&Table, "#"), "Found a character that shouldn't appear\n");
    ok(strlen(Text) == i + 1, "Didn't enumerate enough characters\n");
    del = 0;
    for (i = 0; Text[i]; i++) {
        if (NewE[i] == '1') {
            BOOLEAN WasDeleted;
            WasDeleted = RtlDeleteElementGenericTable(&Table, &Text[i]);
            del += WasDeleted;
        }
    }
    ok(!RtlNumberGenericTableElements(&Table), "Not zero elements\n");
    ok(!RtlGetElementGenericTable(&Table, 0), "Elements left when we removed them all\n");
    ok(strlen(Text) == del + 1, "Deleted too many times\n");
    ok(IsListEmpty(&Allocations), "Didn't free all memory\n");
}
Example #6
0
PRADIX_TABLE_ELEMENT
RadixLookupElement (
    IN PRTL_GENERIC_TABLE   Table,
    IN ULONG                Key
    )
{
    RADIX_TABLE_ELEMENT     element;

    element.Key = Key;
    return (PRADIX_TABLE_ELEMENT) 
            RtlLookupElementGenericTable(Table, &element);
}
Example #7
0
/*++////////////////////////////////////////////////////////////////////////////

ClassCompleteRequest()

Routine Description:

    This routine is a wrapper around (and should be used instead of)
    IoCompleteRequest.  It is used primarily for debugging purposes.
    The routine will assert if the Irp being completed is still holding
    the release lock.

Arguments:

    DeviceObject - the device object that was handling this request

    Irp - the irp to be completed by IoCompleteRequest

    PriorityBoost - the priority boost to pass to IoCompleteRequest

Return Value:

    none

--*/
VOID
ClassCompleteRequest(
    _In_ PDEVICE_OBJECT DeviceObject,
    _In_ PIRP Irp,
    _In_ CCHAR PriorityBoost
    )
{
    #if DBG
        PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;

        PRTL_GENERIC_TABLE removeTrackingList = NULL;
        REMOVE_TRACKING_BLOCK searchDataBlock;
        PREMOVE_TRACKING_BLOCK foundTrackingBlock;

        KIRQL oldIrql;

        KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);

        removeTrackingList = commonExtension->RemoveTrackingList;

        if (removeTrackingList != NULL)
        {
            searchDataBlock.Tag = Irp;

            foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock);

            if(foundTrackingBlock != NULL) {

                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
                            "Irp %p completed while still holding the remove lock\n", Irp));
                TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
                            "Lock acquired in file %s on line %d\n",
                            foundTrackingBlock->File, foundTrackingBlock->Line));
                NT_ASSERT(FALSE);
            }  
        }

        KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
    #endif


    UNREFERENCED_PARAMETER(DeviceObject);

    IoCompleteRequest(Irp, PriorityBoost);
    return;
} // end ClassCompleteRequest()
Example #8
0
static
PCACHE_SECTION_PAGE_TABLE
NTAPI
MiSectionPageTableGet(PRTL_GENERIC_TABLE Table,
                      PLARGE_INTEGER FileOffset)
{
    LARGE_INTEGER SearchFileOffset;
    PCACHE_SECTION_PAGE_TABLE PageTable;
    SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart,
                                           ENTRIES_PER_ELEMENT * PAGE_SIZE);
    PageTable = RtlLookupElementGenericTable(Table, &SearchFileOffset);

    DPRINT("MiSectionPageTableGet(%p,%I64x)\n",
           Table,
           FileOffset->QuadPart);

    return PageTable;
}
Example #9
0
/*
 * @implemented
 * @Mcb: #PLARGE_MCB initialized by FsRtlInitializeLargeMcb().
 * %NULL value is forbidden.
 * @Vbn: Starting virtual block number to specify the range to delete.
 * @SectorCount: Length of the range to delete.
 * Value less or equal to %0 is forbidden; FIXME: Is the reject of %0 W32 compliant?
 *
 * Deletes any possible @Mcb mappings in the given range @Vbn ... @Vbn+@SectorCount-1.
 * This call has no problems if no mappings exist there yet.
 */
BOOLEAN
NTAPI
FsRtlRemoveBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
                        IN LONGLONG Vbn,
                        IN LONGLONG SectorCount)
{
    PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
    LARGE_MCB_MAPPING_ENTRY NeedleRun;
    PLARGE_MCB_MAPPING_ENTRY HaystackRun;

    if (Vbn < 0 || SectorCount <= 0) return FALSE;
    if (Vbn + SectorCount <= Vbn) return FALSE;

    NeedleRun.RunStartVbn.QuadPart = Vbn;
    NeedleRun.RunEndVbn.QuadPart = Vbn + SectorCount;
    NeedleRun.StartingLbn.QuadPart = ~0ULL;

    /* adjust/destroy all intersecting ranges */
    Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
    while ((HaystackRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)))
    {
        if (HaystackRun->RunStartVbn.QuadPart < NeedleRun.RunStartVbn.QuadPart)
        {
            ASSERT(HaystackRun->RunEndVbn.QuadPart > NeedleRun.RunStartVbn.QuadPart);
            HaystackRun->RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart;
        }
        else if (HaystackRun->RunEndVbn.QuadPart > NeedleRun.RunEndVbn.QuadPart)
        {
            ASSERT(HaystackRun->RunStartVbn.QuadPart < NeedleRun.RunEndVbn.QuadPart);
            HaystackRun->RunStartVbn.QuadPart = NeedleRun.RunEndVbn.QuadPart;
        }
        else
        {
            ASSERT(NeedleRun.RunStartVbn.QuadPart >= HaystackRun->RunStartVbn.QuadPart);
            //ASSERT(NeedleRun.RunEndVbn.QuadPart <= HaystackRun->RunEndVbn.QuadPart);
            Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
            RtlDeleteElementGenericTable(&Mcb->Mapping->Table, HaystackRun);
            Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
        }
    }
    Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;

    return TRUE;
}
Example #10
0
/*
 * @implemented
 */
NTSTATUS
NTAPI
FsRtlFastUnlockSingle(IN PFILE_LOCK FileLock,
                      IN PFILE_OBJECT FileObject,
                      IN PLARGE_INTEGER FileOffset,
                      IN PLARGE_INTEGER Length,
                      IN PEPROCESS Process,
                      IN ULONG Key,
                      IN PVOID Context OPTIONAL,
                      IN BOOLEAN AlreadySynchronized)
{
    BOOLEAN FoundShared = FALSE;
    PLIST_ENTRY SharedEntry;
    PLOCK_SHARED_RANGE SharedRange = NULL;
    COMBINED_LOCK_ELEMENT Find;
    PCOMBINED_LOCK_ELEMENT Entry;
    PIRP NextMatchingLockIrp;
    PLOCK_INFORMATION InternalInfo = FileLock->LockInformation;
    DPRINT("FsRtlFastUnlockSingle(%wZ, Offset %08x%08x (%d), Length %08x%08x (%d), Key %x)\n", 
           &FileObject->FileName, 
           FileOffset->HighPart,
           FileOffset->LowPart, 
           (int)FileOffset->QuadPart,
           Length->HighPart,
           Length->LowPart,
           (int)Length->QuadPart,
           Key);
    // The region to unlock must correspond exactly to a previously locked region
    // -- msdn
    // But Windows 2003 doesn't assert on it and simply ignores that parameter
    // ASSERT(AlreadySynchronized);
    Find.Exclusive.FileLock.StartingByte = *FileOffset;
    Find.Exclusive.FileLock.EndingByte.QuadPart = 
        FileOffset->QuadPart + Length->QuadPart;
    if (!InternalInfo) {
        DPRINT("File not previously locked (ever)\n");
        return STATUS_RANGE_NOT_LOCKED;
    }
    Entry = RtlLookupElementGenericTable(&InternalInfo->RangeTable, &Find);
    if (!Entry) {
        DPRINT("Range not locked %wZ\n", &FileObject->FileName);
        return STATUS_RANGE_NOT_LOCKED;
    }

    DPRINT("Found lock entry: Exclusive %u %08x%08x:%08x%08x %wZ\n",
           Entry->Exclusive.FileLock.ExclusiveLock,
           Entry->Exclusive.FileLock.StartingByte.HighPart,
           Entry->Exclusive.FileLock.StartingByte.LowPart,
           Entry->Exclusive.FileLock.EndingByte.HighPart,
           Entry->Exclusive.FileLock.EndingByte.LowPart,
           &FileObject->FileName);
    
    if (Entry->Exclusive.FileLock.ExclusiveLock)
    {
        if (Entry->Exclusive.FileLock.Key != Key ||
            Entry->Exclusive.FileLock.ProcessId != Process->UniqueProcessId ||
            Entry->Exclusive.FileLock.StartingByte.QuadPart != FileOffset->QuadPart ||
            Entry->Exclusive.FileLock.EndingByte.QuadPart != 
            FileOffset->QuadPart + Length->QuadPart)
        {
            DPRINT("Range not locked %wZ\n", &FileObject->FileName);
            return STATUS_RANGE_NOT_LOCKED;
        }
        RtlCopyMemory(&Find, Entry, sizeof(Find));
        // Remove the old exclusive lock region
        RtlDeleteElementGenericTable(&InternalInfo->RangeTable, Entry);
    }
    else
    {
        DPRINT("Shared lock %wZ Start %08x%08x End %08x%08x\n", 
               &FileObject->FileName,
               Entry->Exclusive.FileLock.StartingByte.HighPart,
               Entry->Exclusive.FileLock.StartingByte.LowPart,
               Entry->Exclusive.FileLock.EndingByte.HighPart,
               Entry->Exclusive.FileLock.EndingByte.LowPart);
        for (SharedEntry = InternalInfo->SharedLocks.Flink;
             SharedEntry != &InternalInfo->SharedLocks;
             SharedEntry = SharedEntry->Flink)
        {
            SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
            if (SharedRange->Start.QuadPart == FileOffset->QuadPart &&
                SharedRange->End.QuadPart == FileOffset->QuadPart + Length->QuadPart &&
                SharedRange->Key == Key &&
                SharedRange->ProcessId == Process->UniqueProcessId)
            {
                FoundShared = TRUE;
                DPRINT("Found shared element to delete %wZ Start %08x%08x End %08x%08x Key %x\n",
                       &FileObject->FileName,
                       SharedRange->Start.HighPart,
                       SharedRange->Start.LowPart,
                       SharedRange->End.HighPart,
                       SharedRange->End.LowPart,
                       SharedRange->Key);
                break;
            }
        }
        if (FoundShared)
        {
            /* Remove the found range from the shared range lists */
            RemoveEntryList(&SharedRange->Entry);
            ExFreePoolWithTag(SharedRange, TAG_RANGE);
            /* We need to rebuild the list of shared ranges. */
            DPRINT("Removing the lock entry %wZ (%08x%08x:%08x%08x)\n", 
                   &FileObject->FileName, 
                   Entry->Exclusive.FileLock.StartingByte.HighPart, 
                   Entry->Exclusive.FileLock.StartingByte.LowPart,
                   Entry->Exclusive.FileLock.EndingByte.HighPart, 
                   Entry->Exclusive.FileLock.EndingByte.LowPart);
               
            /* Remember what was in there and remove it from the table */
            Find = *Entry;
            RtlDeleteElementGenericTable(&InternalInfo->RangeTable, &Find);
            /* Put shared locks back in place */
            for (SharedEntry = InternalInfo->SharedLocks.Flink;
                 SharedEntry != &InternalInfo->SharedLocks;
                 SharedEntry = SharedEntry->Flink)
            {
                COMBINED_LOCK_ELEMENT LockElement;
                SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
                LockElement.Exclusive.FileLock.FileObject = FileObject;
                LockElement.Exclusive.FileLock.StartingByte = SharedRange->Start;
                LockElement.Exclusive.FileLock.EndingByte = SharedRange->End;
                LockElement.Exclusive.FileLock.ProcessId = SharedRange->ProcessId;
                LockElement.Exclusive.FileLock.Key = SharedRange->Key;
                LockElement.Exclusive.FileLock.ExclusiveLock = FALSE;
                
                if (LockCompare(&InternalInfo->RangeTable, &Find, &LockElement) != GenericEqual)
                {
                    DPRINT("Skipping range %08x%08x:%08x%08x\n",
                           LockElement.Exclusive.FileLock.StartingByte.HighPart,
                           LockElement.Exclusive.FileLock.StartingByte.LowPart,
                           LockElement.Exclusive.FileLock.EndingByte.HighPart,
                           LockElement.Exclusive.FileLock.EndingByte.LowPart);
                    continue;
                }
                DPRINT("Re-creating range %08x%08x:%08x%08x\n",
                       LockElement.Exclusive.FileLock.StartingByte.HighPart,
                       LockElement.Exclusive.FileLock.StartingByte.LowPart,
                       LockElement.Exclusive.FileLock.EndingByte.HighPart,
                       LockElement.Exclusive.FileLock.EndingByte.LowPart);
                FsRtlpRebuildSharedLockRange(FileLock, InternalInfo, &LockElement);
            }
        }
        else
        {
            return STATUS_RANGE_NOT_LOCKED;
        }
    }

#ifndef NDEBUG    
    DPRINT("Lock still has:\n");
    for (SharedEntry = InternalInfo->SharedLocks.Flink;
         SharedEntry != &InternalInfo->SharedLocks;
         SharedEntry = SharedEntry->Flink)
    {
        SharedRange = CONTAINING_RECORD(SharedEntry, LOCK_SHARED_RANGE, Entry);
        DPRINT("Shared element %wZ Offset %08x%08x Length %08x%08x Key %x\n",
               &FileObject->FileName,
               SharedRange->Start.HighPart,
               SharedRange->Start.LowPart,
               SharedRange->End.HighPart,
               SharedRange->End.LowPart,
               SharedRange->Key);
    }
#endif
    
    // this is definitely the thing we want
    InternalInfo->Generation++;
    while ((NextMatchingLockIrp = IoCsqRemoveNextIrp(&InternalInfo->Csq, &Find)))
    {
        if (NextMatchingLockIrp->IoStatus.Information == InternalInfo->Generation)
        {
            // We've already looked at this one, meaning that we looped.  
            // Put it back and exit.
            IoCsqInsertIrpEx
                (&InternalInfo->Csq,
                 NextMatchingLockIrp,
                 NULL,
                 NULL);
            break;
        }
        // Got a new lock irp... try to do the new lock operation
        // Note that we pick an operation that would succeed at the time
        // we looked, but can't guarantee that it won't just be re-queued
        // because somebody else snatched part of the range in a new thread.
        DPRINT("Locking another IRP %p for %p %wZ\n", 
               &FileObject->FileName, FileLock, NextMatchingLockIrp);
        FsRtlProcessFileLock(InternalInfo->BelongsTo, NextMatchingLockIrp, NULL);
    }
    
    DPRINT("Success %wZ\n", &FileObject->FileName);
    return STATUS_SUCCESS;
}
Example #11
0
/*
 * @implemented
 * @Mcb: #PLARGE_MCB initialized by FsRtlInitializeLargeMcb().
 * %NULL value is forbidden.
 * @Vbn: Starting virtual block number of the wished range.
 * @Lbn: Starting logical block number of the wished range.
 * @SectorCount: Length of the wished range.
 * Value less or equal to %0 is forbidden; FIXME: Is the reject of %0 W32 compliant?
 *
 * Adds the specified range @Vbn ... @Vbn+@SectorCount-1 to @Mcb.
 * Any mappings previously in this range are deleted first.
 *
 * Returns: %TRUE if successful.
 */
BOOLEAN
NTAPI
FsRtlAddBaseMcbEntry(IN PBASE_MCB OpaqueMcb,
                     IN LONGLONG Vbn,
                     IN LONGLONG Lbn,
                     IN LONGLONG SectorCount)
{
    PBASE_MCB_INTERNAL Mcb = (PBASE_MCB_INTERNAL)OpaqueMcb;
    LARGE_MCB_MAPPING_ENTRY Node, NeedleRun;
    PLARGE_MCB_MAPPING_ENTRY LowerRun, HigherRun;
    BOOLEAN NewElement;

    if (Vbn < 0) return FALSE;
    if (SectorCount <= 0) return FALSE;

    /* clean any possible previous entries in our range */
    FsRtlRemoveBaseMcbEntry(OpaqueMcb, Vbn, SectorCount);

    // We need to map [Vbn, Vbn+SectorCount) to [Lbn, Lbn+SectorCount),
    // taking in account the fact that we need to merge these runs if
    // they are adjacent or overlap, but fail if new run fully fits into another run

    /* initially we think we will be inserted as a separate run */
    Node.RunStartVbn.QuadPart = Vbn;
    Node.RunEndVbn.QuadPart = Vbn + SectorCount;
    Node.StartingLbn.QuadPart = Lbn;

    /* optionally merge with lower run */
    NeedleRun.RunStartVbn.QuadPart = Node.RunStartVbn.QuadPart - 1;
    NeedleRun.RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart + 1;
    NeedleRun.StartingLbn.QuadPart = ~0ULL;
    Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
    if ((LowerRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)))
    {
        ASSERT(LowerRun->RunEndVbn.QuadPart == Node.RunStartVbn.QuadPart);
        Node.RunStartVbn.QuadPart = LowerRun->RunStartVbn.QuadPart;
        Node.StartingLbn.QuadPart = LowerRun->StartingLbn.QuadPart;
        Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
        RtlDeleteElementGenericTable(&Mcb->Mapping->Table, LowerRun);
        DPRINT("Intersecting lower run found (%I64d,%I64d) Lbn: %I64d\n", LowerRun->RunStartVbn.QuadPart, LowerRun->RunEndVbn.QuadPart, LowerRun->StartingLbn.QuadPart);
    }

    /* optionally merge with higher run */
    NeedleRun.RunStartVbn.QuadPart = Node.RunEndVbn.QuadPart;
    NeedleRun.RunEndVbn.QuadPart = NeedleRun.RunStartVbn.QuadPart + 1;
    Mcb->Mapping->Table.CompareRoutine = McbMappingIntersectCompare;
    if ((HigherRun = RtlLookupElementGenericTable(&Mcb->Mapping->Table, &NeedleRun)))
    {
        ASSERT(HigherRun->RunStartVbn.QuadPart == Node.RunEndVbn.QuadPart);
        Node.RunEndVbn.QuadPart = HigherRun->RunEndVbn.QuadPart;
        Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;
        RtlDeleteElementGenericTable(&Mcb->Mapping->Table, HigherRun);
        DPRINT("Intersecting higher run found (%I64d,%I64d) Lbn: %I64d\n", HigherRun->RunStartVbn.QuadPart, HigherRun->RunEndVbn.QuadPart, HigherRun->StartingLbn.QuadPart);
    }
    Mcb->Mapping->Table.CompareRoutine = McbMappingCompare;

    /* finally insert the resulting run */
    RtlInsertElementGenericTable(&Mcb->Mapping->Table, &Node, sizeof(Node), &NewElement);
    ASSERT(NewElement);
    Node.RunStartVbn.QuadPart = Vbn;
    Node.RunEndVbn.QuadPart = Vbn + SectorCount;
    Node.StartingLbn.QuadPart = Lbn;

    // NB: Two consecutive runs can only be merged, if actual LBNs also match!

    /* 1.
            Existing->RunStartVbn
            |
            |///////|
                |/////////////|
                |
                Node->RunStartVbn

        2.
            Existing->RunStartVbn
            |
            |///////|
        |//////|
        |
        Node->RunStartVbn

        3.
            Existing->RunStartVbn
            |
            |///////|
                |///|
                |
                Node->RunStartVbn

        4.
            Existing->RunStartVbn
            |
            |///////|
        |///////////////|
        |
        Node->RunStartVbn


    Situation with holes:
    1. Holes at both ends
    2. Hole at the right, new run merged with the previous run
    3. Hole at the right, new run is not merged with the previous run
    4. Hole at the left, new run merged with the next run
    5. Hole at the left, new run is not merged with the next run
    6. No holes, exact fit to merge with both previous and next runs
    7. No holes, merges only with the next run
    8. No holes, merges only with the previous run
    9. No holes, does not merge with next or prev runs


    Overwriting existing mapping is not possible and results in FALSE being returned
    */
    return TRUE;
}