Windows2000/private/ntos/io/pnprlist.c

1310 lines
35 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
pnprlist.c
Abstract:
This module contains routines to manipulate relations list. Relation lists
are used by Plug and Play during the processing of device removal and
ejection.
These routines are all pageable and can't be called at raised IRQL or with
a spinlock held.
Author:
Robert Nelson (robertn) Apr, 1998.
Revision History:
--*/
#include "iop.h"
#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'lrpP')
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IopAddRelationToList)
#pragma alloc_text(PAGE, IopAllocateRelationList)
#pragma alloc_text(PAGE, IopCompressRelationList)
#pragma alloc_text(PAGE, IopEnumerateRelations)
#pragma alloc_text(PAGE, IopFreeRelationList)
#pragma alloc_text(PAGE, IopGetRelationsCount)
#pragma alloc_text(PAGE, IopGetRelationsTaggedCount)
#pragma alloc_text(PAGE, IopIsRelationInList)
#pragma alloc_text(PAGE, IopMergeRelationLists)
#pragma alloc_text(PAGE, IopRemoveIndirectRelationsFromList)
#pragma alloc_text(PAGE, IopRemoveRelationFromList)
#pragma alloc_text(PAGE, IopSetAllRelationsTags)
#pragma alloc_text(PAGE, IopSetRelationsTag)
#endif
#define RELATION_FLAGS 0x00000003
#define RELATION_FLAG_TAGGED 0x00000001
#define RELATION_FLAG_DESCENDANT 0x00000002
NTSTATUS
IopAddRelationToList(
IN PRELATION_LIST List,
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN DirectDescendant,
IN BOOLEAN Tagged
)
/*++
Routine Description:
Adds an element to a relation list.
If this is the first DeviceObject of a particular level then a new
RELATION_LIST_ENTRY will be allocated.
This routine should only be called on an uncompressed relation list,
otherwise it is likely that STATUS_INVALID_PARAMETER will be returned.
Arguments:
List Relation list to which the DeviceObject is added.
DeviceObject DeviceObject to be added to List. It must be a
PhysicalDeviceObject (PDO).
DirectDescendant Indicates whether DeviceObject is a direct descendant of
the original target device of this remove.
Tagged Indicates whether DeviceObject should be tagged in List.
Return Value:
STATUS_SUCCESS
The DeviceObject was added successfully.
STATUS_OBJECT_NAME_COLLISION
The DeviceObject already exists in the relation list.
STATUS_INSUFFICIENT_RESOURCES
There isn't enough PagedPool available to allocate a new
RELATION_LIST_ENTRY.
STATUS_INVALID_PARAMETER
The level of the DEVICE_NODE associated with DeviceObject is less than
FirstLevel or greater than the MaxLevel.
STATUS_NO_SUCH_DEVICE
DeviceObject is not a PhysicalDeviceObject (PDO), it doesn't have a
DEVICE_NODE associated with it.
--*/
{
PDEVICE_NODE deviceNode;
PRELATION_LIST_ENTRY entry;
ULONG level;
ULONG index;
ULONG flags;
PAGED_CODE();
flags = 0;
if (Tagged) {
Tagged = 1;
flags |= RELATION_FLAG_TAGGED;
}
if (DirectDescendant) {
flags |= RELATION_FLAG_DESCENDANT;
}
if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
level = deviceNode->Level;
// Since this routine is called with the DeviceNode Tree locked and
// List is initially allocated with enough entries to hold the deepest
// DEVICE_NODE this ASSERT should never fire. If it does then either
// the tree is changing or we were given a compressed list.
ASSERT(List->FirstLevel <= level && level <= List->MaxLevel);
if (List->FirstLevel <= level && level <= List->MaxLevel) {
if ((entry = List->Entries[ level - List->FirstLevel ]) == NULL) {
// This is the first DeviceObject of its level, allocate a new
// RELATION_LIST_ENTRY.
entry = ExAllocatePool( PagedPool, sizeof(RELATION_LIST_ENTRY) + IopNumberDeviceNodes * sizeof(PDEVICE_OBJECT));
if (entry == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// We always allocate enough Devices to hold the whole tree as
// a simplification. Since each entry is a PDEVICE_OBJECT and
// there is generally under 50 devices on a machine this means
// under 1K for each entry. The excess space will be freed when
// the list is compressed.
entry->Count = 0;
entry->MaxCount = IopNumberDeviceNodes;
List->Entries[ level - List->FirstLevel ] = entry;
}
// There should always be room for a DeviceObject since the Entry is
// initially dimensioned large enough to hold all the DEVICE_NODES
// in the system.
ASSERT(entry->Count < entry->MaxCount);
if (entry->Count < entry->MaxCount) {
// Search the list to see if DeviceObject has already been
// added.
for (index = 0; index < entry->Count; index++) {
if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
// DeviceObject already exists in the list. However
// the Direct Descendant flag may differ. We will
// override it if DirectDescendant is TRUE. This could
// happen if we merged two relation lists.
if (DirectDescendant) {
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_DESCENDANT);
}
return STATUS_OBJECT_NAME_COLLISION;
}
}
} else {
// There isn't room in the Entry for another DEVICE_OBJECT, the
// list has probably already been compressed.
return STATUS_INVALID_PARAMETER;
}
// Take out a reference on DeviceObject, we will release it when we
// free the list or remove the DeviceObject from the list.
ObReferenceObject( DeviceObject );
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)DeviceObject | flags);
entry->Count++;
List->Count++;
List->TagCount += Tagged;
return STATUS_SUCCESS;
} else {
// There isn't an Entry available for the level of this
// DEVICE_OBJECT, the list has probably already been compressed.
return STATUS_INVALID_PARAMETER;
}
} else {
// DeviceObject is not a PhysicalDeviceObject (PDO).
return STATUS_NO_SUCH_DEVICE;
}
}
PRELATION_LIST
IopAllocateRelationList(
VOID
)
/*++
Routine Description:
Allocate a new Relations List. The list is initially sized large enough to
hold the deepest DEVICE_NODE encountered since the system started.
Arguments:
NONE
Return Value:
Newly allocated list if enough PagedPool is available, otherwise NULL.
--*/
{
PRELATION_LIST list;
ULONG maxLevel;
ULONG listSize;
PAGED_CODE();
// Level number of the deepest DEVICE_NODE allocated since the system
// started.
maxLevel = IopMaxDeviceNodeLevel;
listSize = sizeof(RELATION_LIST) + maxLevel * sizeof(PRELATION_LIST_ENTRY);
if ((list = ExAllocatePool( PagedPool, listSize )) != NULL) {
RtlZeroMemory(list, listSize);
// list->FirstLevel = 0;
// list->Count = 0;
// list->Tagged = 0;
list->MaxLevel = maxLevel;
}
return list;
}
NTSTATUS
IopCompressRelationList(
IN OUT PRELATION_LIST *List
)
/*++
Routine Description:
Compresses the relation list by reallocating the list and all the entries so
that they a just large enough to hold their current contents.
Once a list has been compressed IopAddRelationToList and
IopMergeRelationLists targetting this list are both likely to fail.
Arguments:
List Relation List to compress.
Return Value:
STATUS_SUCCESS
The list was compressed. Although this routine does allocate memory and
the allocation can fail, the routine itself will never fail. Since the
memory we are allocating is always smaller then the memory it is
replacing we just keep the old memory if the allocation fails.
--*/
{
PRELATION_LIST oldList, newList;
PRELATION_LIST_ENTRY oldEntry, newEntry;
ULONG lowestLevel;
ULONG highestLevel;
ULONG index;
PAGED_CODE();
oldList = *List;
// Initialize lowestLevel and highestLevel with illegal values chosen so
// that the first real entry will override them.
lowestLevel = oldList->MaxLevel;
highestLevel = oldList->FirstLevel;
// Loop through the list looking for allocated entries.
for (index = 0; index <= (oldList->MaxLevel - oldList->FirstLevel); index++) {
if ((oldEntry = oldList->Entries[ index ]) != NULL) {
// This entry is allocated, update lowestLevel and highestLevel if
// necessary.
if (lowestLevel > index) {
lowestLevel = index;
}
if (highestLevel < index) {
highestLevel = index;
}
if (oldEntry->Count < oldEntry->MaxCount) {
// This entry is only partially full. Allocate a new entry
// which is just the right size to hold the current number of PDEVICE_OBJECTs.
newEntry = ExAllocatePool( PagedPool, sizeof(RELATION_LIST_ENTRY) + (oldEntry->Count - 1) * sizeof(PDEVICE_OBJECT));
if (newEntry != NULL) {
// Initialize Count and MaxCount to the number of
// PDEVICE_OBJECTs in the old entry.
newEntry->Count = oldEntry->Count;
newEntry->MaxCount = oldEntry->Count;
// Copy the PDEVICE_OBJECTs from the old entry to the new one.
RtlCopyMemory( newEntry->Devices,
oldEntry->Devices,
oldEntry->Count * sizeof(PDEVICE_OBJECT));
// Free the old entry and store the new entry in the list.
ExFreePool( oldEntry );
oldList->Entries[ index ] = newEntry;
}
}
}
}
// Assert that the old list isn't empty.
ASSERT(lowestLevel <= highestLevel);
if (lowestLevel > highestLevel) {
// The list is empty - we shouldn't get asked to compress an empty list
// but lets do it anyways.
lowestLevel = 0;
highestLevel = 0;
}
// Check if the old list had unused entries at the beginning or the end of
// the Entries array.
if (lowestLevel != oldList->FirstLevel || highestLevel != oldList->MaxLevel) {
// Allocate a new List with just enough Entries to hold those between
// FirstLevel and MaxLevel inclusive.
newList = ExAllocatePool( PagedPool, sizeof(RELATION_LIST) + (highestLevel - lowestLevel) * sizeof(PRELATION_LIST_ENTRY));
if (newList != NULL) {
// Copy the old list to the new list and return it to the caller.
newList->Count = oldList->Count;
newList->TagCount = oldList->TagCount;
newList->FirstLevel = lowestLevel;
newList->MaxLevel = highestLevel;
RtlCopyMemory( newList->Entries,
&oldList->Entries[ lowestLevel ],
(highestLevel - lowestLevel + 1) * sizeof(PRELATION_LIST_ENTRY));
ExFreePool( oldList );
*List = newList;
}
}
return STATUS_SUCCESS;
}
BOOLEAN
IopEnumerateRelations(
IN PRELATION_LIST List,
IN OUT PULONG Marker,
OUT PDEVICE_OBJECT *DeviceObject,
OUT BOOLEAN *DirectDescendant, OPTIONAL
OUT BOOLEAN *Tagged, OPTIONAL
IN BOOLEAN Reverse
)
/*++
Routine Description:
Enumerates the relations in a list.
Arguments:
List Relation list to be enumerated.
Marker Cookie used to maintain current place in the list. It
must be initialized to 0 the first time
IopEnumerateRelations is called.
DeviceObject Returned Relation.
DirectDescendant If specified then it is set if the relation is a direct
descendant of the original target device of this remove.
Tagged If specified then it is set if the relation is tagged
otherwise it is cleared.
Reverse Direction of traversal, TRUE means from deepest to
closest to the root, FALSE means from the root down.
If Reverse changes on a subsequent call then the
previously enumerated relation is skipped. For example,
given the sequence A, B, C, D, E. If
IopEnumerateRelations is called thrice with Reverse set
to FALSE and then called repeatedly with Reverse set to
TRUE until it returns FALSE, the sequence would be: A,
B, C, B, A.
Once the end has been reached it is not possible to
change directions.
Return Value:
TRUE
DeviceObject and optionally Tagged have been set to the next relation.
FALSE
There are no more relations.
--*/
{
PRELATION_LIST_ENTRY entry;
LONG levelIndex;
ULONG entryIndex;
PAGED_CODE();
// The basic assumptions of our use of Marker is that there will never be
// more than 16M DeviceNodes at any one level and that the tree will never
// be more than 127 deep.
// The format of Marker is
// Bit 31 = Valid (used to distinguish the initial call
// Bit 30-24 = Current index into entries
// Bit 23-0 = Current index into devices, 0xFFFFFF means last
if (*Marker == ~0U) {
// We've reached the end.
return FALSE;
}
if (*Marker == 0) {
// This is the initial call to IopEnumerateRelations
if (Reverse) {
// Initialize levelIndex to the last element of Entries
levelIndex = List->MaxLevel - List->FirstLevel;
} else {
// Initialize levelIndex to the first element of Entries
levelIndex = 0;
}
// Initialize entryIndex to unknown element of Devices. If we are going
// in reverse then this will appear to be beyond the last element and
// we'll adjust it the last one. If we are going forward then this will
// appear to be just prior to the first element so when we increment it,
// it will become zero.
entryIndex = ~0U;
} else {
// Bit 31 is our valid bit, used to distinguish level 0, device 0 from
// the first time call.
ASSERT(*Marker & ((ULONG)1 << 31));
// Current level stored in bits 30-24.
levelIndex = (*Marker >> 24) & 0x7F;
// Current device stored in bits 23-0.
entryIndex = *Marker & 0x00FFFFFF;
}
if (Reverse) {
// We are traversing the list bottom up, from the deepest device towards
// the root.
for ( ; levelIndex >= 0; levelIndex--) {
// Since the Entries array can be sparse find the next allocated
// Entry.
if ((entry = List->Entries[ levelIndex ]) != NULL) {
if (entryIndex > entry->Count) {
// entryIndex (the current one) is greater than Count, this
// will be the case where it is 0xFFFFFF, in other words
// unspecified. Adjust it so that it is one past the last
// one in this Entry.
entryIndex = entry->Count;
}
if (entryIndex > 0) {
// The current entry is beyond the first entry so the next
// entry (which is the one we are looking for is immediately
// prior, adjust entryIndex.
entryIndex--;
// Get the device object and remove the tag.
*DeviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS);
if (Tagged != NULL) {
// The caller is interested in the tag value.
*Tagged = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_TAGGED);
}
if (DirectDescendant != NULL) {
// The caller is interested in the DirectDescendant value.
*DirectDescendant = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_DESCENDANT);
}
// Update the marker (info for current device)
*Marker = ((ULONG)1 << 31) | (levelIndex << 24) | (entryIndex & 0x00FFFFFF);
return TRUE;
}
}
// The current device object has been deleted or the current
// device object is the first one in this Entry.
// We need to continue to search backwards through the other
// Entries.
entryIndex = ~0U;
}
} else {
for ( ; levelIndex <= (LONG)(List->MaxLevel - List->FirstLevel); levelIndex++) {
// Since the Entries array can be sparse find the next allocated
// Entry.
if ((entry = List->Entries[ levelIndex ]) != NULL) {
// entryIndex is the index of the current device or 0xFFFFFFFF
// if this is the first time we have been called or the current
// current device is the last one in its Entry. Increment the
// index to point to the next device.
entryIndex++;
if (entryIndex < entry->Count) {
// The next device is within this entry.
// Get the device object and remove the tag.
*DeviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS);
if (Tagged != NULL) {
// The caller is interested in the tag value.
*Tagged = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_TAGGED);
}
if (DirectDescendant != NULL) {
// The caller is interested in the DirectDescendant value.
*DirectDescendant = (BOOLEAN)((ULONG_PTR)entry->Devices[ entryIndex ] & RELATION_FLAG_DESCENDANT);
}
// Update the marker (info for current device)
*Marker = ((ULONG)1 << 31) | (levelIndex << 24) | (entryIndex & 0x00FFFFFF);
return TRUE;
}
}
// The current device has been removed or we have processed the
// last device in the current entry.
// Set entryIndex so that it is just before the first device in
// the next entry. Continue the search looking for the next
// allocated Entry.
entryIndex = ~0U;
}
}
// We are at the end of the list
*Marker = ~0U;
*DeviceObject = NULL;
if (Tagged != NULL) {
*Tagged = FALSE;
}
if (DirectDescendant != NULL) {
*DirectDescendant = FALSE;
}
return FALSE;
}
VOID
IopFreeRelationList(
IN PRELATION_LIST List
)
/*++
Routine Description:
Free a relation list allocated by IopAllocateRelationList.
Arguments:
List The list to be freed.
Return Value:
NONE.
--*/
{
PRELATION_LIST_ENTRY entry;
ULONG levelIndex;
ULONG entryIndex;
PAGED_CODE();
// Search the list looking for allocated Entries.
for (levelIndex = 0; levelIndex <= (List->MaxLevel - List->FirstLevel); levelIndex++) {
if ((entry = List->Entries[ levelIndex ]) != NULL) {
// This entry has been allocated.
for (entryIndex = 0; entryIndex < entry->Count; entryIndex++) {
// Dereference all the Devices in the entry.
ObDereferenceObject((PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS));
}
// Free the Entry.
ExFreePool( entry );
}
}
// Free the list. It isn't necessary to dereference the DeviceObject that
// was the original target that caused the list to be created. This
// DeviceObject is also in one of the Entries and its reference is taken
// and released there.
ExFreePool( List );
}
ULONG
IopGetRelationsCount(
PRELATION_LIST List
)
/*++
Routine Description:
Returns the total number of relations (Device Objects) in all the entries.
Arguments:
List Relation List.
Return Value:
Count of relations (Device Objects).
--*/
{
PAGED_CODE();
return List->Count;
}
ULONG
IopGetRelationsTaggedCount(
PRELATION_LIST List
)
/*++
Routine Description:
Returns the total number of relations (Device Objects) in all the entries
which are tagged.
Arguments:
List Relation List.
Return Value:
Count of tagged relations (Device Objects).
--*/
{
PAGED_CODE();
return List->TagCount;
}
BOOLEAN
IopIsRelationInList(
PRELATION_LIST List,
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Checks if a relation (Device Object) exists in the specified relation list.
Arguments:
List Relation list to check.
DeviceObject Relation to be checked.
Return Value:
TRUE
Relation exists.
FALSE
Relation is not in the list.
--*/
{
PDEVICE_NODE deviceNode;
PRELATION_LIST_ENTRY entry;
ULONG level;
ULONG index;
PAGED_CODE();
if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
// The device object is a PDO.
level = deviceNode->Level;
if (List->FirstLevel <= level && level <= List->MaxLevel) {
// The level is within the range of levels stored in this list.
if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
// There is an Entry for this level.
for (index = 0; index < entry->Count; index++) {
// For each Device in the entry, compare it to the given
// DeviceObject
if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
// It matches
return TRUE;
}
}
}
}
}
// It wasn't a PDO
// or the level wasn't in the range of levels in this list
// or there are no DeviceObjects at the same level in this list
// or the DeviceObject isn't in the Entry for its level in this list
return FALSE;
}
NTSTATUS
IopMergeRelationLists(
IN OUT PRELATION_LIST TargetList,
IN PRELATION_LIST SourceList,
IN BOOLEAN Tagged
)
/*++
Routine Description:
Merges two relation lists by copying all the relations from the source list
to the target list. Source list remains unchanged.
Arguments:
TargetList List to which the relations from Sourcelist are added.
SourceList List of relations to be added to TargetList.
Tagged TRUE if relations from SourceList should be tagged when added to
TargetList. If FALSE then relations added from SourceList are
untagged.
Return Value:
STATUS_SUCCESS
All the relations in SourceList were added to TargetList successfully.
STATUS_OBJECT_NAME_COLLISION
One of the relations in SourceList already exists in TargetList. This
is a fatal error and TargetList may already have some of the relations
from SourceList added. This could be dealt with more gracefully if
necessary but the current callers of IopMergeRelationLists avoid this
situation.
STATUS_INSUFFICIENT_RESOURCES
There isn't enough PagedPool available to allocate a new
RELATION_LIST_ENTRY.
STATUS_INVALID_PARAMETER
The level of one of the relations in SourceList is less than FirstLevel
or greater than the MaxLevel. This is a fatal error and TargetList may
already have some of the relations from SourceList added. The only way
this could happen is if the tree lock isn't held or if TargetList has
been compressed by IopCompressRelationList. Both situations would be
bugs in the caller.
STATUS_NO_SUCH_DEVICE
One of the relations in SourceList is not a PhysicalDeviceObject (PDO),
it doesn't have a DEVICE_NODE associated with it. This is a fatal error
and TargetList may already have some of the relations from SourceList
added. This should never happen since it was a PDO when it was added to
SourceList.
--*/
{
PRELATION_LIST_ENTRY entry;
ULONG levelIndex;
ULONG entryIndex;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
// For each level entry in SourceList
for (levelIndex = 0;
levelIndex <= (SourceList->MaxLevel - SourceList->FirstLevel);
levelIndex++) {
if ((entry = SourceList->Entries[ levelIndex ]) != NULL) {
// The Entry has Devices
for (entryIndex = 0; entryIndex < entry->Count; entryIndex++) {
// For each Device in the Entry, add it to the target List.
status = IopAddRelationToList( TargetList,
(PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ entryIndex ] & ~RELATION_FLAGS),
FALSE,
Tagged);
// BUGBUG - Need to handle STATUS_INSUFFICIENT_RESOURCES more
// gracefully. Currently the only way this can happen is
// during Eject and that code isn't enabled yet.
ASSERT(NT_SUCCESS(status));
if (!NT_SUCCESS(status)) {
// BUGBUG - We should undo the damage we've done to
// TargetList by removing those relations we've added from
// SourceList.
break;
}
}
}
}
return status;
}
NTSTATUS
IopRemoveIndirectRelationsFromList(
IN PRELATION_LIST List
)
/*++
Routine Description:
Removes all the relations without the DirectDescendant flag from a relation
list.
Arguments:
List List from which to remove the relations.
Return Value:
STATUS_SUCCESS
The relations were removed successfully.
--*/
{
PDEVICE_OBJECT deviceObject;
PRELATION_LIST_ENTRY entry;
ULONG level;
LONG index;
PAGED_CODE();
// For each Entry in the list.
for (level = List->FirstLevel; level <= List->MaxLevel; level++) {
// If the entry is allocated.
if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
// For each Device in the list.
for (index = entry->Count - 1; index >= 0; index--) {
if (!((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_DESCENDANT)) {
deviceObject = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS);
ObDereferenceObject( deviceObject );
if ((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) {
List->TagCount--;
}
if (index < ((LONG)entry->Count - 1)) {
RtlMoveMemory( &entry->Devices[ index ],
&entry->Devices[ index + 1 ],
(entry->Count - index - 1) * sizeof(PDEVICE_OBJECT));
}
if (--entry->Count == 0) {
List->Entries[ level - List->FirstLevel ] = NULL;
ExFreePool(entry);
}
List->Count--;
}
}
}
}
return STATUS_SUCCESS;
}
NTSTATUS
IopRemoveRelationFromList(
PRELATION_LIST List,
PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Removes a relation from a relation list.
Arguments:
List List from which to remove the relation.
DeviceObject Relation to remove.
Return Value:
STATUS_SUCCESS
The relation was removed successfully.
STATUS_NO_SUCH_DEVICE
The relation doesn't exist in the list.
--*/
{
PDEVICE_NODE deviceNode;
PRELATION_LIST_ENTRY entry;
ULONG level;
LONG index;
PAGED_CODE();
if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
level = deviceNode->Level;
ASSERT(List->FirstLevel <= level && level <= List->MaxLevel);
if (List->FirstLevel <= level && level <= List->MaxLevel) {
if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
for (index = entry->Count - 1; index >= 0; index--) {
if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
ObDereferenceObject( DeviceObject );
if (((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) != 0) {
List->TagCount--;
}
if (index < ((LONG)entry->Count - 1)) {
RtlMoveMemory( &entry->Devices[ index ],
&entry->Devices[ index + 1 ],
(entry->Count - index - 1) * sizeof(PDEVICE_OBJECT));
}
if (--entry->Count == 0) {
List->Entries[ level - List->FirstLevel ] = NULL;
ExFreePool(entry);
}
List->Count--;
return STATUS_SUCCESS;
}
}
}
}
}
return STATUS_NO_SUCH_DEVICE;
}
VOID
IopSetAllRelationsTags(
PRELATION_LIST List,
BOOLEAN Tagged
)
/*++
Routine Description:
Tags or untags all the relations in a relations list.
Arguments:
List Relation list containing relations to be tagged or untagged.
Tagged TRUE if the relations should be tagged, FALSE if they are to be
untagged.
Return Value:
NONE
--*/
{
PRELATION_LIST_ENTRY entry;
ULONG level;
ULONG index;
PAGED_CODE();
// For each Entry in the list.
for (level = List->FirstLevel; level <= List->MaxLevel; level++) {
// If the entry is allocated.
if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
// For each Device in the list.
for (index = 0; index < entry->Count; index++) {
// Set or clear the tag based on the argument Tagged.
if (Tagged) {
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_TAGGED);
} else {
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAG_TAGGED);
}
}
}
}
// If we are setting the tags then update the TagCount to the number of
// relations in the list. Otherwise reset it to zero.
List->TagCount = Tagged ? List->Count : 0;
}
NTSTATUS
IopSetRelationsTag(
IN PRELATION_LIST List,
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Tagged
)
/*++
Routine Description:
Sets or clears a tag on a specified relation in a relations list. This
routine is also used by some callers to determine if a relation exists in
a list and if so to set the tag.
Arguments:
List List containing relation to be tagged or untagged.
DeviceObject Relation to be tagged or untagged.
Tagged TRUE if relation is to be tagged, FALSE if it is to be
untagged.
Return Value:
STATUS_SUCCESS
The relation was tagged successfully.
STATUS_NO_SUCH_DEVICE
The relation doesn't exist in the list.
--*/
{
PDEVICE_NODE deviceNode;
PRELATION_LIST_ENTRY entry;
ULONG level;
LONG index;
PAGED_CODE();
if ((deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode) != NULL) {
// DeviceObject is a PhysicalDeviceObject (PDO), get its level.
level = deviceNode->Level;
if (List->FirstLevel <= level && level <= List->MaxLevel) {
// The level is within the range of levels in this List.
if ((entry = List->Entries[ level - List->FirstLevel ]) != NULL) {
// The Entry for this level is allocated. Search each device
// in the Entry looking for a match.
for (index = entry->Count - 1; index >= 0; index--) {
if (((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAGS) == (ULONG_PTR)DeviceObject) {
// We found a match
if ((ULONG_PTR)entry->Devices[ index ] & RELATION_FLAG_TAGGED) {
// The relation is already tagged so to simplify the
// logic below decrement the TagCount. We'll
// increment it later if the caller still wants it
// to be tagged.
List->TagCount--;
}
if (Tagged) {
// Set the tag and increment the number of tagged
// relations.
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] | RELATION_FLAG_TAGGED);
List->TagCount++;
} else {
// Clear the tag.
entry->Devices[ index ] = (PDEVICE_OBJECT)((ULONG_PTR)entry->Devices[ index ] & ~RELATION_FLAG_TAGGED);
}
return STATUS_SUCCESS;
}
}
}
}
}
// It wasn't a PDO
// or the level wasn't in the range of levels in this list
// or there are no DeviceObjects at the same level in this list
// or the DeviceObject isn't in the Entry for its level in this list
return STATUS_NO_SUCH_DEVICE;
}