/*++ Copyright (c) 1997 Microsoft Corporation Module Name: range.c Abstract: Kernel-mode range list support for arbiters Author: Andy Thornton (andrewth) 02/17/97 Revision History: --*/ #include "ntrtlp.h" #include "range.h" #if DBG // Debug print level: // -1 = no messages // 0 = vital messages only // 1 = call trace // 2 = verbose messages LONG RtlRangeDebugLevel = 0; #endif NTSTATUS RtlpAddRange( IN OUT PLIST_ENTRY ListHead, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ); NTSTATUS RtlpAddToMergedRange( IN PRTLP_RANGE_LIST_ENTRY Merged, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ); NTSTATUS RtlpConvertToMergedRange( IN PRTLP_RANGE_LIST_ENTRY Entry ); PRTLP_RANGE_LIST_ENTRY RtlpCreateRangeListEntry( IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR Attributes, IN PVOID UserData, IN PVOID Owner ); NTSTATUS RtlpAddIntersectingRanges( IN PLIST_ENTRY ListHead, IN PRTLP_RANGE_LIST_ENTRY First, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ); NTSTATUS RtlpDeleteFromMergedRange( IN PRTLP_RANGE_LIST_ENTRY Delete, IN PRTLP_RANGE_LIST_ENTRY Merged ); PRTLP_RANGE_LIST_ENTRY RtlpCopyRangeListEntry( PRTLP_RANGE_LIST_ENTRY Entry ); VOID RtlpDeleteRangeListEntry( IN PRTLP_RANGE_LIST_ENTRY Entry ); BOOLEAN RtlpIsRangeAvailable( IN PRTL_RANGE_LIST_ITERATOR Iterator, IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR AttributeAvailableMask, IN BOOLEAN SharedOK, IN BOOLEAN NullConflictOK, IN BOOLEAN Forward, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL ); #if DBG VOID RtlpDumpRangeListEntry( LONG Level, PRTLP_RANGE_LIST_ENTRY Entry, BOOLEAN Indent ); VOID RtlpDumpRangeList( LONG Level, PRTL_RANGE_LIST RangeList ); #else #define RtlpDumpRangeListEntry(Level, Entry, Indent) #define RtlpDumpRangeList(Level, RangeList) #endif // DBG // Make everything pageable or init #ifdef ALLOC_PRAGMA #if defined(NTOS_KERNEL_RUNTIME) #pragma alloc_text(INIT, RtlInitializeRangeListPackage) #endif #pragma alloc_text(PAGE, RtlpAddRange) #pragma alloc_text(PAGE, RtlpAddToMergedRange) #pragma alloc_text(PAGE, RtlpConvertToMergedRange) #pragma alloc_text(PAGE, RtlpCreateRangeListEntry) #pragma alloc_text(PAGE, RtlpAddIntersectingRanges) #pragma alloc_text(PAGE, RtlpDeleteFromMergedRange) #pragma alloc_text(PAGE, RtlpCopyRangeListEntry) #pragma alloc_text(PAGE, RtlpDeleteRangeListEntry) #pragma alloc_text(PAGE, RtlpIsRangeAvailable) #if DBG #pragma alloc_text(PAGE, RtlpDumpRangeListEntry) #pragma alloc_text(PAGE, RtlpDumpRangeList) #endif #pragma alloc_text(PAGE, RtlInitializeRangeList) #pragma alloc_text(PAGE, RtlAddRange) #pragma alloc_text(PAGE, RtlDeleteRange) #pragma alloc_text(PAGE, RtlDeleteOwnersRanges) #pragma alloc_text(PAGE, RtlCopyRangeList) #pragma alloc_text(PAGE, RtlFreeRangeList) #pragma alloc_text(PAGE, RtlIsRangeAvailable) #pragma alloc_text(PAGE, RtlFindRange) #pragma alloc_text(PAGE, RtlGetFirstRange) #pragma alloc_text(PAGE, RtlGetNextRange) #pragma alloc_text(PAGE, RtlMergeRangeLists) #pragma alloc_text(PAGE, RtlInvertRangeList) #endif // ALLOC_PRAGMA // Range List memory allocation #if defined(NTOS_KERNEL_RUNTIME) // The kernel mode range list API uses a lookaside list to speed allocation // of range list entries. The PAGED_LOOKASIDE_LIST structure should be non-paged. #define RTLP_RANGE_LIST_ENTRY_LOOKASIDE_DEPTH 16 PAGED_LOOKASIDE_LIST RtlpRangeListEntryLookasideList; VOID RtlInitializeRangeListPackage( VOID ) /*++ Routine Description: This routine initializes the stuctures required by the range list APIs. It is called during system initialization (Phase1Initialization) and should be before any of the range list apis are called. Arguments: None. Return Value: None. --*/ { ExInitializePagedLookasideList( &RtlpRangeListEntryLookasideList, NULL, NULL, 0, sizeof(RTLP_RANGE_LIST_ENTRY), RTL_RANGE_LIST_ENTRY_TAG, RTLP_RANGE_LIST_ENTRY_LOOKASIDE_DEPTH ); } // PRANGE_LIST_ENTRY RtlpAllocateRangeListEntry(VOID) #define RtlpAllocateRangeListEntry() \ (PRTLP_RANGE_LIST_ENTRY) ExAllocateFromPagedLookasideList( \ &RtlpRangeListEntryLookasideList \ ) // VOID // RtlpFreeRangeListEntry( // IN PRTLP_RANGE_LIST_ENTRY Entry // ) #define RtlpFreeRangeListEntry(Entry) \ ExFreeToPagedLookasideList(&RtlpRangeListEntryLookasideList, (Entry)) // PVOID RtlpRangeListAllocatePool(IN ULONG Size) #define RtlpRangeListAllocatePool(Size) \ ExAllocatePoolWithTag(PagedPool, (Size), RTL_RANGE_LIST_MISC_TAG) // VOID RtlpRangeListFreePool(IN PVOID Free) #define RtlpRangeListFreePool(Free) \ ExFreePool(Free) #else // defined(NTOS_KERNEL_RUNTIME) // Usermode range lists use the standard Rtl heap for allocations // PRANGE_LIST_ENTRY RtlpAllocateRangeListEntry(VOID); #define RtlpAllocateRangeListEntry() \ (PRTLP_RANGE_LIST_ENTRY) RtlAllocateHeap( \ RtlProcessHeap(), \ RTL_RANGE_LIST_ENTRY_TAG, \ sizeof(RTLP_RANGE_LIST_ENTRY) \ ) // VOID RtlpFreeRangeListEntry(IN PRTLP_RANGE_LIST_ENTRY Entry) #define RtlpFreeRangeListEntry(Entry) \ RtlFreeHeap( RtlProcessHeap(), 0, (Entry) ) // PVOID RtlpRangeListAllocatePool(IN ULONG Size) #define RtlpRangeListAllocatePool(Size) RtlAllocateHeap(RtlProcessHeap(), RTL_RANGE_LIST_MISC_TAG, (Size)) // VOID RtlpRangeListFreePool(IN PVOID Free) #define RtlpRangeListFreePool(Free) \ RtlFreeHeap( RtlProcessHeap(), 0, (Free) ) #endif // defined(NTOS_KERNEL_RUNTIME) VOID RtlInitializeRangeList( IN OUT PRTL_RANGE_LIST RangeList ) /*++ Routine Description: This routine initializes a range list. It must be called before the range list is passed to any of the other range list functions. Initially the range list contains no ranges Arguments: RangeList - Pointer to a user allocated RTL_RANGE_LIST structre to be initialized. Return Value: None. --*/ { RTL_PAGED_CODE(); ASSERT(RangeList); DEBUG_PRINT(1, ("RtlInitializeRangeList(0x%08x)\n", RangeList)); InitializeListHead(&RangeList->ListHead); RangeList->Flags = 0; RangeList->Count = 0; RangeList->Stamp = 0; } NTSTATUS RtlAddRange( IN OUT PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR Attributes, IN ULONG Flags, IN PVOID UserData, OPTIONAL IN PVOID Owner OPTIONAL ) /*++ Routine Description: This routine adds a new range with the specified properties to a range list. Arguments: RangeList - Pointer to the range list to which the new range is to be added. It must have been previously initialized using RtlInitializeRangeList. Start - The location of the start of the new range. End - The location of the end of the new range. Flags - These determine the range's properties and how it is added: RTL_RANGE_LIST_ADD_IF_CONFLICT - The range should be added even if it overlaps another range. In this case the RTL_RANGE_CONFLICT flag is set. RTL_RANGE_LIST_ADD_SHARED - The range is marked as an RTL_RANGE_SHARED and will successfully be added if it overlaps another shared range. It can be speficied in conjunction with the above ADD_IF_CONFLICT flag in which case if the range overlaps a non-shared range it will be marked as both RTL_RANGE_SHARED and RTL_RANGE_CONFLICT. UserData - Extra data to be stored with the range. The system will not attempt to interpret it. Owner - A cookie that represents the entity that owns this range. (A pointer to some object is the most likley). The system will not attempt to interpret it, just use it to distinguish the range from another with the same start and end. Return Value: Status code that indicates whether or not the function was successful: STATUS_INVALID_PARAMETER STATUS_RANGE_LIST_CONFLICT STATUS_INSUFFICIENT_RESOURCES --*/ { NTSTATUS status; PRTLP_RANGE_LIST_ENTRY newEntry = NULL; RTL_PAGED_CODE(); DEBUG_PRINT(1, ("RtlAddRange(0x%08x, 0x%I64x, 0x%I64x, 0x%08x, 0x%08x, 0x%08x)\n", RangeList, Start, End, Flags, UserData, Owner )); // Validate parameters if (End < Start) { return STATUS_INVALID_PARAMETER; } // Create a new entry if (!(newEntry = RtlpCreateRangeListEntry(Start, End, Attributes, UserData, Owner))) { return STATUS_INSUFFICIENT_RESOURCES; } // Mark the new entry as shared if appropriate if (Flags & RTL_RANGE_LIST_ADD_SHARED) { newEntry->PublicFlags |= RTL_RANGE_SHARED; } status = RtlpAddRange(&RangeList->ListHead, newEntry, Flags ); if (NT_SUCCESS(status)) { // We added a range so bump the count RangeList->Count++; RangeList->Stamp++; } else { // We didn't add a range so free up the entry RtlpFreeRangeListEntry(newEntry); } return status; } NTSTATUS RtlpAddRange( IN OUT PLIST_ENTRY ListHead, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ) /*++ Routine Description: This routine implement the AddRange operation adding the range in the appropriate place in the sorted range list, converting ranges to merged ranges and setting RTL_RANGE_CONFLICT flags as necessary. Arguments: ListHead - The list of the range list to which the range should be added. Entry - The new entry to be added to the range list AddRangeFlags - The Flags argument to RtlAddRange, see above. Return Value: Status code that indicates whether or not the function was successful: STATUS_RANGE_LIST_CONFLICT STATUS_INSUFFICIENT_RESOURCES --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY previous, current; ULONGLONG start, end; DEBUG_PRINT(2, ("RtlpAddRange(0x%08x, 0x%08x{0x%I64x-0x%I64x}, 0x%08x)\n", ListHead, Entry, Entry->Start, Entry->End, AddRangeFlags )); RTL_PAGED_CODE(); ASSERT(Entry); start = Entry->Start; end = Entry->End; // Clear the conflict flag if it was left around Entry->PublicFlags &= ~RTL_RANGE_CONFLICT; // Iterate through the list and find where to insert the entry FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, ListHead, current) { if (end < current->Start) { // The new range is completely before this one DEBUG_PRINT(2, ("Completely before\n")); InsertEntryList(current->ListEntry.Blink, &Entry->ListEntry ); goto exit; } else if (RANGE_INTERSECT(Entry, current)) { status = RtlpAddIntersectingRanges(ListHead, current, Entry, AddRangeFlags); goto exit; } } // New range is after all existing ranges DEBUG_PRINT(2, ("After all existing ranges\n")); InsertTailList(ListHead, &Entry->ListEntry ); exit: return status; } NTSTATUS RtlpAddToMergedRange( IN PRTLP_RANGE_LIST_ENTRY Merged, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ) /*++ Routine Description: This routine adds a new range to a merged range, setting the RTL_RANGE_CONFLICT flags if necessary. Arguments: Merged - The merged range to which Entry should be added. Entry - The new entry to be added to the range list AddRangeFlags - The Flags argument to RtlAddRange, see above. Return Value: Status code that indicates whether or not the function was successful: STATUS_RANGE_LIST_CONFLICT - indictates that the range was not added because it conflicted with another range and conflicts are not allowed --*/ { PRTLP_RANGE_LIST_ENTRY current; PLIST_ENTRY insert = NULL; BOOLEAN entryShared; RTL_PAGED_CODE(); ASSERT(Merged); ASSERT(Entry); ASSERT(MERGED(Merged)); entryShared = SHARED(Entry); // Insert it into the merged list, this is sorted in order of start FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &Merged->Merged.ListHead, current) { // Do we conflict? if (RANGE_INTERSECT(current, Entry) && !(entryShared && SHARED(current))) { // Are conflicts ok? if (AddRangeFlags & RTL_RANGE_LIST_ADD_IF_CONFLICT) { // Yes - Mark both entries as conflicting current->PublicFlags |= RTL_RANGE_CONFLICT; Entry->PublicFlags |= RTL_RANGE_CONFLICT; } else { // No - Fail return STATUS_RANGE_LIST_CONFLICT; } } // Have we not yet found the insertion point and just passed it? if (!insert && current->Start > Entry->Start) { // Insert is before current insert = current->ListEntry.Blink; } } // Did we find where to insert the new range? if (!insert) { // New range is after all existing ranges InsertTailList(&Merged->Merged.ListHead, &Entry->ListEntry ); } else { // Insert in the list InsertEntryList(insert, &Entry->ListEntry ); } // Expand the merged range if necessary if (Entry->Start < Merged->Start) { Merged->Start = Entry->Start; } if (Entry->End > Merged->End) { Merged->End = Entry->End; } // If we just added a shared range to a completely shared merged // range then the shared flag can stay otherwise it must go if (SHARED(Merged) && !entryShared) { DEBUG_PRINT(2, ("RtlpAddToMergedRange: Merged range no longer completely shared\n")); Merged->PublicFlags &= ~RTL_RANGE_SHARED; } return STATUS_SUCCESS; } NTSTATUS RtlpConvertToMergedRange( IN PRTLP_RANGE_LIST_ENTRY Entry ) /*++ Routine Description: This converts a non-merged range into a merged range with one member, the range that was just converted. Arguments: Entry - The entry to be converted into a merged range. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES --*/ { PRTLP_RANGE_LIST_ENTRY newEntry; RTL_PAGED_CODE(); ASSERT(Entry); ASSERT(!MERGED(Entry)); ASSERT(!CONFLICT(Entry)); // Create a new entry if (!(newEntry = RtlpCopyRangeListEntry(Entry))) { return STATUS_INSUFFICIENT_RESOURCES; } // Convert the current entry into a merged one NB. Throw away all the // private flags but leave the public ones as they can only be shared. InitializeListHead(&Entry->Merged.ListHead); Entry->PrivateFlags = RTLP_RANGE_LIST_ENTRY_MERGED; ASSERT(Entry->PublicFlags == RTL_RANGE_SHARED || Entry->PublicFlags == 0); // Add the range InsertHeadList(&Entry->Merged.ListHead, &newEntry->ListEntry ); return STATUS_SUCCESS; } PRTLP_RANGE_LIST_ENTRY RtlpCreateRangeListEntry( IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR Attributes, IN PVOID UserData, IN PVOID Owner ) /*++ Routine Description: This routine allocates a new range list entry and fills it in the the data provided. Arguments: Start - The location of the start of the new range. End - The location of the end of the new range. Attributes - Extra data (normally flags) to be stored with the range. The system will not attempt to interpret it. UserData - Extra data to be stored with the range. The system will not attempt to interpret it. Owner - A cookie that represents the entity that owns this range. (A pointer to some object is the most likley). The system will not attempt to interpret it, just use it to distinguish the range from another with the same start and end. Return Value: Pointer to the new range list entry or NULL if a new entry could not be allocated. --*/ { PRTLP_RANGE_LIST_ENTRY entry; RTL_PAGED_CODE(); ASSERT(Start <= End); // Allocate a new entry if (entry = RtlpAllocateRangeListEntry()) { // Fill in the details #if DBG entry->ListEntry.Flink = NULL; entry->ListEntry.Blink = NULL; #endif entry->PublicFlags = 0; entry->PrivateFlags = 0; entry->Start = Start; entry->End = End; entry->Allocated.UserData = UserData; entry->Allocated.Owner = Owner; entry->Attributes = Attributes; } return entry; } NTSTATUS RtlpAddIntersectingRanges( IN PLIST_ENTRY ListHead, IN PRTLP_RANGE_LIST_ENTRY First, IN PRTLP_RANGE_LIST_ENTRY Entry, IN ULONG AddRangeFlags ) /*++ Routine Description: This routine adds a range to a range list when the new range overlaps an existing range. Ranges are converted to mergedranges and the RTL_RANGE_CONFLICT flag is set as necessary. Arguments: ListHead - The list of the range list to which the range should be added. First - The first range that intersects Entry - The new range to be added AddRangeFlags - The Flags argument to RtlAddRange, see above. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES STATUS_RANGE_LIST_CONFLICT --*/ { NTSTATUS status; PRTLP_RANGE_LIST_ENTRY current, next, currentMerged, nextMerged; BOOLEAN entryShared; RTL_PAGED_CODE(); ASSERT(First); ASSERT(Entry); entryShared = SHARED(Entry); // If we care about conflicts see if we conflict with anyone if (!(AddRangeFlags & RTL_RANGE_LIST_ADD_IF_CONFLICT)) { // Examine all ranges after the first intersecting one current = First; FOR_REST_IN_LIST(RTLP_RANGE_LIST_ENTRY, ListHead, current) { if (Entry->End < current->Start) { // We don't intersect anymore so there arn't any more // conflicts break; } else if (MERGED(current)) { // Check if any of the merged ranges conflict FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, ¤t->Merged.ListHead, currentMerged) { // Do we conflict? if (RANGE_INTERSECT(currentMerged, Entry) && !(entryShared && SHARED(currentMerged))) { // We conflict with one of the merged ranges return STATUS_RANGE_LIST_CONFLICT; } } } else if (!(entryShared && SHARED(current))) { // We conflict with a non shared region in the main list. return STATUS_RANGE_LIST_CONFLICT; } } } // Ok - either we didn't find any conflicts or we don't care about // them. Now its safe to perform the merge. Make the first // overlapping range into a header if it is not already one and then // add the rest of the ranges if (!MERGED(First)) { status = RtlpConvertToMergedRange(First); if (!NT_SUCCESS(status)) { goto cleanup; } } ASSERT(MERGED(First)); current = RANGE_LIST_ENTRY_FROM_LIST_ENTRY(First->ListEntry.Flink); // Consider the entries between the one following first and the last // intersecting one. FOR_REST_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, ListHead, current, next) { if (Entry->End < current->Start) { // We don't intersect any more break; } if (MERGED(current)) { // Add all the merged ranges to the new entry FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, ¤t->Merged.ListHead, currentMerged, nextMerged) { // Remove the entry from the current list RemoveEntryList(¤tMerged->ListEntry); // Add the entry to the new merged range status = RtlpAddToMergedRange(First, currentMerged, AddRangeFlags ); // We should not be able to fail the add but just to be // on the safe side... ASSERT(NT_SUCCESS(status)); } // Remove and free the now empty header ASSERT(IsListEmpty(¤t->Merged.ListHead)); RemoveEntryList(¤t->ListEntry); RtlpFreeRangeListEntry(current); } else { // Remove the entry from the main list RemoveEntryList(¤t->ListEntry); // Add the entry to the new merged range status = RtlpAddToMergedRange(First, current, AddRangeFlags ); // We should not be able to fail the add but just to be // on the safe side... ASSERT(NT_SUCCESS(status)); } } // Finally add the entry that did the overlapping status = RtlpAddToMergedRange(First, Entry, AddRangeFlags ); ASSERT(NT_SUCCESS(status)); cleanup: return status; } NTSTATUS RtlDeleteRange( IN OUT PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN PVOID Owner ) /*++ Routine Description: This routine deletes a range from a range list. Arguments: Start - The location of the start of the range to be deleted. End - The location of the end of the range to be deleted. Owner - The owner of the range to be deleted, used to distinguish the range from another with the same start and end. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES STATUS_RANGE_LIST_CONFLICT --*/ { NTSTATUS status = STATUS_RANGE_NOT_FOUND; PRTLP_RANGE_LIST_ENTRY current, next, currentMerged, nextMerged; RTL_PAGED_CODE(); ASSERT(RangeList); DEBUG_PRINT(1, ("RtlDeleteRange(0x%08x, 0x%I64x, 0x%I64x, 0x%08x)\n", RangeList, Start, End, Owner )); FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, current, next) { // We're passed all possible intersections if (End < current->Start) { // We didn't find a match break; } if (MERGED(current)) { // COuld our range exist in this merged range? if (Start >= current->Start && End <= current->End) { FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, ¤t->Merged.ListHead, currentMerged, nextMerged) { if (currentMerged->Start == Start && currentMerged->End == End && currentMerged->Allocated.Owner == Owner) { // This is the range - delete it and rebuild the merged // range appropriately status = RtlpDeleteFromMergedRange(currentMerged, current ); goto exit; } } } } else if (current->Start == Start && current->End == End && current->Allocated.Owner == Owner) { // This is the range - delete it! RemoveEntryList(¤t->ListEntry); RtlpFreeRangeListEntry(current); status = STATUS_SUCCESS; goto exit; } } exit: if (NT_SUCCESS(status)) { // We have removed a range so decrement the count in the header RangeList->Count--; RangeList->Stamp++; } return status; } NTSTATUS RtlDeleteOwnersRanges( IN OUT PRTL_RANGE_LIST RangeList, IN PVOID Owner ) /*++ Routine Description: This routine deletes all the ranges owned by a specific owner from a range list. Arguments: Owner - The owner of the ranges to be deleted. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY current, next, currentMerged, nextMerged; RTL_PAGED_CODE(); ASSERT(RangeList); DEBUG_PRINT(1, ("RtlDeleteOwnersRanges(0x%08x, 0x%08x)\n", RangeList, Owner )); findNext: FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, current, next) { if (MERGED(current)) { FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, ¤t->Merged.ListHead, currentMerged, nextMerged) { if (currentMerged->Allocated.Owner == Owner) { // This is the range - delete it and rebuild the merged // range appropriately DEBUG_PRINT(2, ("RtlDeleteOwnersRanges: Deleting merged range \ (Start=%I64x, End=%I64x)\n", currentMerged->Start, currentMerged->End )); status = RtlpDeleteFromMergedRange(currentMerged, current ); if (!NT_SUCCESS(status)) { goto cleanup; } RangeList->Count--; RangeList->Stamp++; // Can't keep scanning the list as it might have changed // underneath us - start from the beginning again... // BUGBUG - could keep a last safe position to go from... goto findNext; } } } else if (current->Allocated.Owner == Owner) { // This is the range - delete it! RemoveEntryList(¤t->ListEntry); RtlpFreeRangeListEntry(current); DEBUG_PRINT(2, ("RtlDeleteOwnersRanges: Deleting range (Start=%I64x,End=%I64x)\n", current->Start, current->End )); RangeList->Count--; RangeList->Stamp++; status = STATUS_SUCCESS; } } cleanup: return status; } NTSTATUS RtlpDeleteFromMergedRange( IN PRTLP_RANGE_LIST_ENTRY Delete, IN PRTLP_RANGE_LIST_ENTRY Merged ) /*++ Routine Description: This routine deletes a range from a merged range and rebuilds the merged range as appropriate. This includes adding new merged and unmerged ranges. If no ranges are left in the merged range it will be deleted. Arguments: BUGBUG these comments are not for this routine. Start - The location of the start of the range to be deleted. End - The location of the end of the range to be deleted. Owner - The owner of the range to be deleted, used to distinguish the range from another with the same start and end. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY current, next; LIST_ENTRY keepList; PLIST_ENTRY previousInsert, nextInsert; RTL_PAGED_CODE(); ASSERT(MERGED(Merged)); // Remove the entry RemoveEntryList(&Delete->ListEntry); // Initialize the temporary list where the new list will be build InitializeListHead(&keepList); // Add the previously merged ranges into the keep list and put // any duplicates of the delete range into the delete list FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &Merged->Merged.ListHead, current, next) { // Add it to the keepList. Explicitly remove the entries from the // list so it is still valid if we need to rebuild it. RemoveEntryList(¤t->ListEntry); // Clear the conflict flag - if there is still a conflict RtlpAddRange // should set it again. current->PublicFlags &= ~RTL_RANGE_CONFLICT; status = RtlpAddRange(&keepList, current, RTL_RANGE_LIST_ADD_IF_CONFLICT ); if (!NT_SUCCESS(status)) { // This should only happen if we run out of pool goto cleanup; } } if (!IsListEmpty(&keepList)) { // Everything went well so splice this temporary list into the // main list where Merged used to be previousInsert = Merged->ListEntry.Blink; nextInsert = Merged->ListEntry.Flink; previousInsert->Flink = keepList.Flink; keepList.Flink->Blink = previousInsert; nextInsert->Blink = keepList.Blink; keepList.Blink->Flink = nextInsert; } else { RemoveEntryList(&Merged->ListEntry); } // Finally free the range we deleted and the merged range we have orphaned RtlpFreeRangeListEntry(Delete); RtlpFreeRangeListEntry(Merged); return status; cleanup: // Things went wrong - should only be a STATUS_INSUFFICIENT_RESOURCES // Reconstruct the list as it was before the call using the keepList and // deleteList. ASSERT(status == STATUS_INSUFFICIENT_RESOURCES); // Add all the ranges we moved to the keepList back into Merged FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &keepList, current, next) { status = RtlpAddToMergedRange(Merged, current, RTL_RANGE_LIST_ADD_IF_CONFLICT ); ASSERT(NT_SUCCESS(status)); } // And the one were meant to delete status = RtlpAddToMergedRange(Merged, Delete, RTL_RANGE_LIST_ADD_IF_CONFLICT ); return status; } PRTLP_RANGE_LIST_ENTRY RtlpCopyRangeListEntry( PRTLP_RANGE_LIST_ENTRY Entry ) /*++ Routine Description: This routine copies a range list entry. If the entry is merged all the member ranges are copied too. Arguments: Entry - the range list entry to be copied. Return Value: Pointer to the new range list entry or NULL if a new entry could not be allocated. --*/ { PRTLP_RANGE_LIST_ENTRY newEntry; RTL_PAGED_CODE(); ASSERT(Entry); if (newEntry = RtlpAllocateRangeListEntry()) { RtlCopyMemory(newEntry, Entry, sizeof(RTLP_RANGE_LIST_ENTRY)); #if DBG newEntry->ListEntry.Flink = NULL; newEntry->ListEntry.Blink = NULL; #endif if (MERGED(Entry)) { // Copy the merged list PRTLP_RANGE_LIST_ENTRY current, newMerged; InitializeListHead(&newEntry->Merged.ListHead); FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &Entry->Merged.ListHead, current) { // Allocate a new entry and copy the contents newMerged = RtlpAllocateRangeListEntry(); if (!newMerged) { goto cleanup; } RtlCopyMemory(newMerged, current, sizeof(RTLP_RANGE_LIST_ENTRY)); // Insert the new entry InsertTailList(&newEntry->Merged.ListHead, &newMerged->ListEntry); } } } return newEntry; cleanup: // Free the partially build copy RtlpDeleteRangeListEntry(newEntry); return NULL; } NTSTATUS RtlCopyRangeList( OUT PRTL_RANGE_LIST CopyRangeList, IN PRTL_RANGE_LIST RangeList ) /*++ Routine Description: This routine copies a range list. Arguments: CopyRangeList - An initialized but empty range list where RangeList should be copied to. RangeList - The range list that is to be copied. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES STATUS_INVALID_PARAMETER --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY current, newEntry, currentMerged, newMerged; RTL_PAGED_CODE(); ASSERT(RangeList); ASSERT(CopyRangeList); DEBUG_PRINT(1, ("RtlCopyRangeList(0x%08x, 0x%08x)\n", CopyRangeList, RangeList )); // Sanity checks... if (CopyRangeList->Count != 0) { return STATUS_INVALID_PARAMETER; } // Copy the header information CopyRangeList->Flags = RangeList->Flags; CopyRangeList->Count = RangeList->Count; CopyRangeList->Stamp = RangeList->Stamp; // Perform the copy FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, current) { // Copy the current entry newEntry = RtlpCopyRangeListEntry(current); if (!newEntry) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // Add it into the list InsertTailList(&CopyRangeList->ListHead, &newEntry->ListEntry); } return status; cleanup: // Free up the partially complete range list RtlFreeRangeList(CopyRangeList); return status; } VOID RtlpDeleteRangeListEntry( IN PRTLP_RANGE_LIST_ENTRY Entry ) /*++ Routine Description: This routine deletes a range list entry - if the entry is merged then all the member ranges will be deleted as well. The entry will not be removed from any list before deletion - this should be done before calling this routine. Arguments: Entry - The entry to be deleted. Return Value: None --*/ { RTL_PAGED_CODE(); if (MERGED(Entry)) { PRTLP_RANGE_LIST_ENTRY current, next; // Free all member ranges first FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &Entry->Merged.ListHead, current, next) { RtlpFreeRangeListEntry(current); } } RtlpFreeRangeListEntry(Entry); } VOID RtlFreeRangeList( IN PRTL_RANGE_LIST RangeList ) /*++ Routine Description: This routine deletes all the ranges in a range list. Arguments: RangeList - the range list to operate on. Return Value: None --*/ { PRTLP_RANGE_LIST_ENTRY current, next; // Sanity checks... RTL_PAGED_CODE(); ASSERT(RangeList); // Clean up the header information RangeList->Flags = 0; RangeList->Count = 0; FOR_ALL_IN_LIST_SAFE(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, current, next) { // Delete the current entry RemoveEntryList(¤t->ListEntry); RtlpDeleteRangeListEntry(current); } } NTSTATUS RtlIsRangeAvailable( IN PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN ULONG Flags, IN UCHAR AttributeAvailableMask, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL, OUT PBOOLEAN Available ) /*++ Routine Description: This routine determines if a given range is available. Arguments: RangeList - The range list to test availability on. Start - The start of the range to test for availability. End - The end of the range to test for availability. Flags - Modify the behaviour of the routine. RTL_RANGE_LIST_SHARED_OK - indicates that shared ranges should be considered to be available. AttributeAvailableMask - Any range encountered with any of these bits set will be consideredto be available. Available - Pointer to a boolean which will be set to TRUE if the range is available, otherwise FALSE; Return Value: Status code that indicates whether or not the function was successful: --*/ { NTSTATUS status; RTL_RANGE_LIST_ITERATOR iterator; PRTL_RANGE dummy; RTL_PAGED_CODE(); ASSERT(RangeList); ASSERT(Available); DEBUG_PRINT(1, ("RtlIsRangeAvailable(0x%08x, 0x%I64x, 0x%I64x, 0x%08x, 0x%08x)\n", RangeList, Start, End, Flags, Available )); // Initialize iterator to the start of the list status = RtlGetFirstRange(RangeList, &iterator, &dummy); if (status == STATUS_NO_MORE_ENTRIES) { // The range list is empty therefore the range is available *Available = TRUE; return STATUS_SUCCESS; } else if (!NT_SUCCESS(status)) { return status; } *Available = RtlpIsRangeAvailable(&iterator, Start, End, AttributeAvailableMask, (BOOLEAN)(Flags & RTL_RANGE_LIST_SHARED_OK), (BOOLEAN)(Flags & RTL_RANGE_LIST_NULL_CONFLICT_OK), TRUE, Context, Callback ); return STATUS_SUCCESS; } BOOLEAN RtlpIsRangeAvailable( IN PRTL_RANGE_LIST_ITERATOR Iterator, IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR AttributeAvailableMask, IN BOOLEAN SharedOK, IN BOOLEAN NullConflictOK, IN BOOLEAN Forward, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL ) /*++ Routine Description: This routine determines if a given range is available. Arguments: Iterator - An iterator set to the first range to test in the range list. Start - The start of the range to test for availability. End - The end of the range to test for availability. AttributeAvailableMask - Any range encountered with any of these bits set will be considered to be available. SharedOK - Indicated whether or not shared ranges are considered to be available. Return Value: TRUE if the range is available, FALSE otherwise. --*/ { PRTL_RANGE current; RTL_PAGED_CODE(); ASSERT(Iterator); FOR_REST_OF_RANGES(Iterator, current, Forward) { // If we have passed all possible intersections then break out. This // can't be done in a merged region because of possible overlaps. if (Forward) { if (!Iterator->MergedHead && End < current->Start) { break; } } else { if (!Iterator->MergedHead && Start > current->End) { break; } } // Do we intersect? if (RANGE_LIMITS_INTERSECT(Start, End, current->Start, current->End)) { DEBUG_PRINT(2, ("Intersection 0x%I64x-0x%I64x and 0x%I64x-0x%I64x\n", Start, End, current->Start, current->End )); // Is the intersection not Ok because it is with a non-shared // region or we don't want a shared region? Or the user said that // it should be considered available because of the user flags set. if (!((SharedOK && (current->Flags & RTL_RANGE_SHARED)) || (current->Attributes & AttributeAvailableMask) || (NullConflictOK && (current->Owner == NULL)) ) ) { // If the caller provided a callback to support extra conflict // semantics call it if (ARGUMENT_PRESENT(Callback)) { if ((*Callback)(Context, (PRTL_RANGE)current)) { DEBUG_PRINT(2, ("User provided callback overrode conflict\n", Start, End, current->Start, current->End )); continue; } } return FALSE; } } } return TRUE; } NTSTATUS RtlFindRange( IN PRTL_RANGE_LIST RangeList, IN ULONGLONG Minimum, IN ULONGLONG Maximum, IN ULONG Length, IN ULONG Alignment, IN ULONG Flags, IN UCHAR AttributeAvailableMask, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL, OUT PULONGLONG Start ) /*++ Routine Description: This routine finds the first available range that meets the criterion specified. Arguments: RangeList - The range list to find a range in. Minimum - The minimum acceptable value of the start of the range. Maximum - The maximum acceptable value of the end of the range. Length - The length of the range required. Alignmnent - The alignment of the start of the range. Flags - Modify the behaviour of the routine. RTL_RANGE_LIST_SHARED_OK - indicates that shared ranges should be considered to be available. AttributeAvailableMask - Any range encountered with any of these bits set will be considered to be available. Start - Pointer to a ULONGLONG where the start value will be returned on success. Return Value: Status code that indicates whether or not the function was successful: STATUS_UNSUCCESSFUL STATUS_INVALID_PARAMETER --*/ { ULONGLONG start, end; RTL_RANGE_LIST_ITERATOR iterator; PRTL_RANGE dummy; BOOLEAN sharedOK, nullConflictOK; RTL_PAGED_CODE(); ASSERT(RangeList); ASSERT(Start); ASSERT(Alignment > 0); ASSERT(Length > 0); DEBUG_PRINT(1, ("RtlFindRange(0x%08x, 0x%I64x, 0x%I64x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", RangeList, Minimum, Maximum, Length, Alignment, Flags, Start )); // Search from high to low, Align start if necessary start = Maximum - (Length - 1); start -= start % Alignment; // Valiate parameters if ((Minimum > Maximum) || (Maximum - Minimum < Length - 1) || (Minimum + Alignment < Minimum) || (start < Minimum) || (Length == 0) || (Alignment == 0)) { return STATUS_INVALID_PARAMETER; } sharedOK = (BOOLEAN) Flags & RTL_RANGE_LIST_SHARED_OK; nullConflictOK = (BOOLEAN) Flags & RTL_RANGE_LIST_NULL_CONFLICT_OK; // Calculate the end end = start + Length - 1; // Initialze the iterator to the end of the list RtlGetLastRange(RangeList, &iterator, &dummy); // Keep trying to find ranges until we run out of room or we // wrap around do { DEBUG_PRINT(2, ("RtlFindRange: Testing range %I64x-%I64x\n", start, end )); if (RtlpIsRangeAvailable(&iterator, start, end, AttributeAvailableMask, sharedOK, nullConflictOK, FALSE, Context, Callback)) { *Start = start; // Assert our result, if we produced one, is in the in // the range specified ASSERT(*Start >= Minimum && *Start + Length - 1 <= Maximum); return STATUS_SUCCESS; } // Find a suitable range starting from the one we conflicted with, // that is the current range in the iterator - this breaks the // abstraction of the iterator in the name of efficiency. start = ((PRTLP_RANGE_LIST_ENTRY)(iterator.Current))->Start; if ((start - Length) > start) { // Wrapped, fail. break; } start -= Length; start -= start % Alignment; end = start + Length - 1; } while ( start >= Minimum ); return STATUS_UNSUCCESSFUL; } NTSTATUS RtlGetFirstRange( IN PRTL_RANGE_LIST RangeList, OUT PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range ) /*++ Routine Description: This routine extracts the first range in a range list. If there are no ranges then STATUS_NO_MORE_ENTRIES is returned. Arguments: RangeList - The range list to operate on. Iterator - On success this contains the state of the iteration and can be passed to RtlGetNextRange. Range - On success this contains a pointer to the first range Return Value: Status code that indicates whether or not the function was successful: STATUS_NO_MORE_ENTRIES --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY first; RTL_PAGED_CODE(); // Fill in the first part of the iterator Iterator->RangeListHead = &RangeList->ListHead; Iterator->Stamp = RangeList->Stamp; if (!IsListEmpty(&RangeList->ListHead)) { first = RANGE_LIST_ENTRY_FROM_LIST_ENTRY(RangeList->ListHead.Flink); // Fill in the iterator and update to point to the first merged // range if we are merged if (MERGED(first)) { ASSERT(!IsListEmpty(&first->Merged.ListHead)); Iterator->MergedHead = &first->Merged.ListHead; Iterator->Current = RANGE_LIST_ENTRY_FROM_LIST_ENTRY( first->Merged.ListHead.Flink ); } else { Iterator->MergedHead = NULL; Iterator->Current = first; } *Range = (PRTL_RANGE) Iterator->Current; } else { Iterator->Current = NULL; Iterator->MergedHead = NULL; *Range = NULL; status = STATUS_NO_MORE_ENTRIES; } return status; } NTSTATUS RtlGetLastRange( IN PRTL_RANGE_LIST RangeList, OUT PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range ) /*++ Routine Description: This routine extracts the first range in a range list. If there are no ranges then STATUS_NO_MORE_ENTRIES is returned. Arguments: RangeList - The range list to operate on. Iterator - On success this contains the state of the iteration and can be passed to RtlGetNextRange. Range - On success this contains a pointer to the first range Return Value: Status code that indicates whether or not the function was successful: STATUS_NO_MORE_ENTRIES --*/ { NTSTATUS status = STATUS_SUCCESS; PRTLP_RANGE_LIST_ENTRY first; RTL_PAGED_CODE(); // Fill in the first part of the iterator Iterator->RangeListHead = &RangeList->ListHead; Iterator->Stamp = RangeList->Stamp; if (!IsListEmpty(&RangeList->ListHead)) { first = RANGE_LIST_ENTRY_FROM_LIST_ENTRY(RangeList->ListHead.Blink); // Fill in the iterator and update to point to the first merged // range if we are merged if (MERGED(first)) { ASSERT(!IsListEmpty(&first->Merged.ListHead)); Iterator->MergedHead = &first->Merged.ListHead; Iterator->Current = RANGE_LIST_ENTRY_FROM_LIST_ENTRY( first->Merged.ListHead.Blink ); } else { Iterator->MergedHead = NULL; Iterator->Current = first; } *Range = (PRTL_RANGE) Iterator->Current; } else { Iterator->Current = NULL; Iterator->MergedHead = NULL; *Range = NULL; status = STATUS_NO_MORE_ENTRIES; } return status; } NTSTATUS RtlGetNextRange( IN OUT PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range, IN BOOLEAN MoveForwards ) /*++ Routine Description: This routine extracts the next range in a range list. If there are no more ranges then STATUS_NO_MORE_ENTRIES is returned. Arguments: Iterator - The iterator filled in by RtlGet{First|Next}Range which will be update on success. Range - On success this contains a pointer to the next range MoveForwards - If true, go forwards thru the list, otherwise, go backwards. Return Value: Status code that indicates whether or not the function was successful: STATUS_NO_MORE_ENTRIES STATUS_INVALID_PARAMETER Note: Add/Delete operations can not be performed on the list between calls to RtlGetFirstRange / RtlGetNextRange and RtlGetNextRange / RtlGetNextRange. If such calls are made the routine will detect and fail the call. --*/ { PRTLP_RANGE_LIST_ENTRY mergedEntry, next; PLIST_ENTRY entry; RTL_PAGED_CODE(); // Make sure that we haven't changed the list between calls if (RANGE_LIST_FROM_LIST_HEAD(Iterator->RangeListHead)->Stamp != Iterator->Stamp) { ASSERTMSG( "RtlGetNextRange: Add/Delete operations have been performed while \ iterating through a list\n", FALSE); return STATUS_INVALID_PARAMETER; } // If we have already reached the end of the list then return if (!Iterator->Current) { *Range = NULL; return STATUS_NO_MORE_ENTRIES; } entry = &((PRTLP_RANGE_LIST_ENTRY)(Iterator->Current))->ListEntry; next = RANGE_LIST_ENTRY_FROM_LIST_ENTRY( MoveForwards ? entry->Flink : entry->Blink); ASSERT(next); // Are we in a merged range? if (Iterator->MergedHead) { // Have we reached the end of the merged range? if (&next->ListEntry == Iterator->MergedHead) { // Get back to the merged entry mergedEntry = CONTAINING_RECORD( Iterator->MergedHead, RTLP_RANGE_LIST_ENTRY, Merged.ListHead ); // Move on to the next entry in the main list next = MoveForwards ? RANGE_LIST_ENTRY_FROM_LIST_ENTRY( mergedEntry->ListEntry.Flink ) : RANGE_LIST_ENTRY_FROM_LIST_ENTRY( mergedEntry->ListEntry.Blink ); Iterator->MergedHead = NULL; } else { // There are merged ranges left - return the next one Iterator->Current = next; *Range = (PRTL_RANGE) next; return STATUS_SUCCESS; } } // Have we reached the end of the main list? if (&next->ListEntry == Iterator->RangeListHead) { // Tell the caller there are no more ranges Iterator->Current = NULL; *Range = NULL; return STATUS_NO_MORE_ENTRIES; } else { // Is the next range merged? if (MERGED(next)) { // Goto the first merged entry ASSERT(!Iterator->MergedHead); Iterator->MergedHead = &next->Merged.ListHead; Iterator->Current = MoveForwards ? RANGE_LIST_ENTRY_FROM_LIST_ENTRY( next->Merged.ListHead.Flink ) : RANGE_LIST_ENTRY_FROM_LIST_ENTRY( next->Merged.ListHead.Blink ); } else { // Go to the next entry in the main list Iterator->Current = RANGE_LIST_ENTRY_FROM_LIST_ENTRY( &next->ListEntry ); } *Range = (PRTL_RANGE) Iterator->Current; } return STATUS_SUCCESS; } NTSTATUS RtlMergeRangeLists( OUT PRTL_RANGE_LIST MergedRangeList, IN PRTL_RANGE_LIST RangeList1, IN PRTL_RANGE_LIST RangeList2, IN ULONG Flags ) /*++ Routine Description: This routine merges two range lists into one. Arguments: MergedRangeList - An empty range list where on success the result of the merge will be placed. RangeList1 - One of the range lists to be merged. RangeList2 - The other the range list to merged. Flags - Modifies the behaviour of the routine: RTL_RANGE_LIST_MERGE_IF_CONFLICT - Merged ranges even if the conflict. Return Value: Status code that indicates whether or not the function was successful: STATUS_INSUFFICIENT_RESOURCES STATUS_RANGE_LIST_CONFLICT --*/ { NTSTATUS status; PRTLP_RANGE_LIST_ENTRY current, currentMerged, newEntry; ULONG addFlags; RTL_PAGED_CODE(); DEBUG_PRINT(1, ("RtlMergeRangeList(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", MergedRangeList, RangeList1, RangeList2, Flags )); // Copy the first range list status = RtlCopyRangeList(MergedRangeList, RangeList1); if (!NT_SUCCESS(status)) { goto cleanup; } // Add all ranges from 2nd list FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &RangeList2->ListHead, current) { if (MERGED(current)) { FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, ¤t->Merged.ListHead, currentMerged) { if (!(newEntry = RtlpCopyRangeListEntry(currentMerged))) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } if (CONFLICT(currentMerged)) { // If a range was already conflicting in then it will conflict in // the merged range list - allow this. addFlags = Flags | RTL_RANGE_LIST_ADD_IF_CONFLICT; } else { addFlags = Flags; } status = RtlpAddRange(&MergedRangeList->ListHead, newEntry, addFlags ); } } else { if (!(newEntry = RtlpCopyRangeListEntry(current))){ status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } if (CONFLICT(current)) { // If a range was already conflicting in then it will conflict in // the merged range list - allow this. addFlags = Flags | RTL_RANGE_LIST_ADD_IF_CONFLICT; } else { addFlags = Flags; } status = RtlpAddRange(&MergedRangeList->ListHead, newEntry, addFlags ); if (!NT_SUCCESS(status)) { goto cleanup; } } } // Correct the count MergedRangeList->Count += RangeList2->Count; MergedRangeList->Stamp += RangeList2->Count; return status; cleanup: // Something went wrong... Free up what we have built of the // new list and return the error RtlFreeRangeList(MergedRangeList); return status; } NTSTATUS RtlInvertRangeList( OUT PRTL_RANGE_LIST InvertedRangeList, IN PRTL_RANGE_LIST RangeList ) /* Routine Description: This inverts a range list so that all areas which are allocated in InvertedRangeList will not be in RangeList, and vice versa. The ranges in InvertedRangeList will all be owned by NULL. Arguments: InvertedRangeList - a pointer to an empty Range List to be filled with the inverted list RangeList - a pointer to the Range List to be inverted Return Value: Status of operation. */ { PRTLP_RANGE_LIST_ENTRY currentRange; ULONGLONG currentStart = 0; NTSTATUS status; RTL_PAGED_CODE(); // if Inverted List does not start out empty, the inverted list // is meaningless ASSERT(InvertedRangeList->Count == 0); // iterate through all elements of the ReverseAllocation // adding the unallocated part before the current element // to the RealAllocation FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, currentRange) { if (currentRange->Start > currentStart) { // we want a NULL range owner to show that the // range is unavailable status = RtlAddRange(InvertedRangeList, currentStart, currentRange->Start-1, 0, // Attributes 0, // Flags 0, // UserData NULL); // Owner if (!NT_SUCCESS(status)) { return status; } } currentStart = currentRange->End + 1; } // add the portion of the address space above the last // element in the ReverseAllocation to the RealAllocation // unless we've wrapped, in which case we've already added // the last element if (currentStart > (currentStart - 1)) { status = RtlAddRange(InvertedRangeList, currentStart, MAX_ULONGLONG, 0, 0, 0, NULL); if (!NT_SUCCESS(status)) { return status; } } return STATUS_SUCCESS; } #if DBG VOID RtlpDumpRangeListEntry( LONG Level, PRTLP_RANGE_LIST_ENTRY Entry, BOOLEAN Indent ) { PWSTR indentString; PRTLP_RANGE_LIST_ENTRY current; RTL_PAGED_CODE(); if (Indent) { indentString = L"\t\t"; } else { indentString = L""; } // Print the range DEBUG_PRINT(Level, ("%sRange (0x%08x): 0x%I64x-0x%I64x\n", indentString, Entry, Entry->Start, Entry->End )); // Print the flags DEBUG_PRINT(Level, ("%s\tPrivateFlags: ", indentString)); if (MERGED(Entry)) { DEBUG_PRINT(Level, ("MERGED ")); } DEBUG_PRINT(Level, ("\n%s\tPublicFlags: ", indentString)); if (SHARED(Entry)) { DEBUG_PRINT(Level, ("SHARED ")); } if (CONFLICT(Entry)) { DEBUG_PRINT(Level, ("CONFLICT ")); } DEBUG_PRINT(Level, ("\n")); if (MERGED(Entry)) { DEBUG_PRINT(Level, ("%sMerged entries:\n", indentString)); // Print the merged entries FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &Entry->Merged.ListHead, current) { RtlpDumpRangeListEntry(Level, current, TRUE); } } else { // Print the other data DEBUG_PRINT(Level, ("%s\tUserData: 0x%08x\n\tOwner: 0x%08x\n", indentString, Entry->Allocated.UserData, Entry->Allocated.Owner )); } } VOID RtlpDumpRangeList( LONG Level, PRTL_RANGE_LIST RangeList ) { PRTLP_RANGE_LIST_ENTRY current, currentMerged; RTL_PAGED_CODE(); DEBUG_PRINT(Level, ("*** Range List (0x%08x) - Count: %i\n", RangeList, RangeList->Count )); FOR_ALL_IN_LIST(RTLP_RANGE_LIST_ENTRY, &RangeList->ListHead, current) { // Print the entry RtlpDumpRangeListEntry(Level, current, FALSE); } } #endif