657 lines
23 KiB
C++
657 lines
23 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: analyze.cxx
|
|
//
|
|
// Contents: program to analyze the lock order table
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// Coupling:
|
|
//
|
|
// Notes:
|
|
//
|
|
// History: 3-16-2000 benl Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#define _NTFS_NTFSDBG_DEFINITIONS_
|
|
#include "lockorder.h"
|
|
|
|
typedef struct {
|
|
LIST_ENTRY Links;
|
|
NTFS_RESOURCE_NAME Resource;
|
|
} RESOURCE_LIST, *PRESOURCE_LIST;
|
|
|
|
typedef struct {
|
|
NTFS_RESOURCE_NAME Resource;
|
|
LIST_ENTRY Links;
|
|
NTFS_OWNERSHIP_STATE EndState;
|
|
} RESOURCE_STATE_LIST, *PRESOURCE_STATE_LIST;
|
|
|
|
//
|
|
// structure used to track the order a state is reached by
|
|
//
|
|
|
|
typedef struct {
|
|
NTFS_OWNERSHIP_STATE State;
|
|
BOOLEAN ReachedByRelease;
|
|
LIST_ENTRY Links; // RESOURCE_LIST
|
|
} RESOURCE_STATE_ORDER, *PRESOURCE_STATE_ORDER;
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CompareRoutine
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [Table] --
|
|
// [First] --
|
|
// [Second] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 5-08-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS CompareRoutine( struct _RTL_GENERIC_TABLE *Table, PVOID First, PVOID Second )
|
|
{
|
|
PULONG FirstUlong = (PULONG)First;
|
|
PULONG SecondUlong = (PULONG)Second;
|
|
|
|
if (*FirstUlong == *SecondUlong) {
|
|
return GenericEqual;
|
|
} else if (*FirstUlong < *SecondUlong) {
|
|
return GenericLessThan;
|
|
} else {
|
|
return GenericGreaterThan;
|
|
}
|
|
} // CompareRoutine
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: AllocateRoutine
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [Table] --
|
|
// [ByteSize] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 5-08-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
PVOID AllocateRoutine( struct _RTL_GENERIC_TABLE *Table, CLONG ByteSize )
|
|
{
|
|
return malloc( ByteSize );
|
|
} // AllocateRoutine
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeRoutine
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [Table] --
|
|
// [Buffer] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 5-08-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID FreeRoutine( struct _RTL_GENERIC_TABLE *Table, PVOID Buffer )
|
|
{
|
|
free( Buffer );
|
|
} // FreeRoutine
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FindOrInsertStateOrder
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [Table] --
|
|
// [Element] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 5-08-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void FindOrInsertStateOrder( PRTL_GENERIC_TABLE StateTable, NTFS_OWNERSHIP_STATE State )
|
|
{
|
|
RESOURCE_STATE_ORDER TemplateState;
|
|
PRESOURCE_STATE_ORDER NewState;
|
|
|
|
TemplateState.State = State;
|
|
if (!(RtlLookupElementGenericTable( StateTable, &TemplateState ))) {
|
|
NewState = (PRESOURCE_STATE_ORDER)RtlInsertElementGenericTable( StateTable, &TemplateState, sizeof( TemplateState ), NULL );
|
|
InitializeListHead( &NewState->Links );
|
|
NewState->ReachedByRelease = FALSE;
|
|
}
|
|
} // FindOrInsertStateOrder
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FindOrInsertStateList
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [StateTable] --
|
|
// [Resource] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 5-08-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void FindOrInsertStateList( PRTL_GENERIC_TABLE StateTable, NTFS_RESOURCE_NAME Resource )
|
|
{
|
|
RESOURCE_STATE_LIST TemplateState;
|
|
PRESOURCE_STATE_LIST NewState;
|
|
|
|
TemplateState.Resource = Resource;
|
|
if (!(RtlLookupElementGenericTable( StateTable, &TemplateState ))) {
|
|
NewState = (PRESOURCE_STATE_LIST)RtlInsertElementGenericTable( StateTable, &TemplateState, sizeof( TemplateState ), NULL );
|
|
InitializeListHead( &NewState->Links );
|
|
}
|
|
} // FindOrInsertStateList
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: main
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [argc] --
|
|
// [argv] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 3-16-2000 benl Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void __cdecl main (int argc, char *argv[])
|
|
{
|
|
int NumTransitions;
|
|
int Index;
|
|
int Index2;
|
|
|
|
PRESOURCE_LIST Entry;
|
|
PRESOURCE_LIST Entry2;
|
|
PRESOURCE_STATE_LIST EntryState;
|
|
PRESOURCE_STATE_LIST EntryState2;
|
|
BOOLEAN Skipped;
|
|
BOOLEAN FoundState;
|
|
BOOLEAN Verbose = FALSE;
|
|
|
|
PRESOURCE_STATE_ORDER State;
|
|
PRESOURCE_STATE_ORDER EndState;
|
|
PRESOURCE_STATE_ORDER EndState2;
|
|
|
|
RTL_GENERIC_TABLE StateTable;
|
|
RTL_GENERIC_TABLE ResourceTable;
|
|
RESOURCE_STATE_ORDER StateOrder;
|
|
|
|
RESOURCE_STATE_LIST StateListOrderTemplate;
|
|
PRESOURCE_STATE_LIST StateListOrder;
|
|
|
|
PVOID Key;
|
|
int StateCheck;
|
|
|
|
|
|
if (argc > 1) {
|
|
if (argv[1][0] == '-') {
|
|
switch (argv[1][1]) {
|
|
case 'v':
|
|
Verbose = TRUE;
|
|
break;
|
|
default:
|
|
printf( "Usage: analyze [-v]\nThis program analyzes ntfs's lock order state table\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Discover transition states
|
|
//
|
|
|
|
RtlInitializeGenericTable( &StateTable, CompareRoutine, AllocateRoutine, FreeRoutine, NULL );
|
|
RtlInitializeGenericTable( &ResourceTable, CompareRoutine, AllocateRoutine, FreeRoutine, NULL );
|
|
|
|
StateOrder.ReachedByRelease = FALSE;
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTable ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
FindOrInsertStateList( &ResourceTable, OwnershipTransitionTable[Index].Acquired );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTable[Index].Begin );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTable[Index].End );
|
|
}
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTableRelease ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableRelease[Index].Acquired );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableRelease[Index].Begin );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableRelease[Index].End );
|
|
}
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTableAcquire ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableAcquire[Index].Acquired );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableAcquire[Index].Begin );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableAcquire[Index].End );
|
|
}
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTableUnsafe ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableUnsafe[Index].Acquired );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableUnsafe[Index].Begin );
|
|
FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableUnsafe[Index].End );
|
|
}
|
|
|
|
printf( "%d states and %d resources referenced in transition tables\n\n",
|
|
StateTable.NumberGenericTableElements,
|
|
ResourceTable.NumberGenericTableElements );
|
|
|
|
/*
|
|
for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, TRUE );
|
|
State != 0;
|
|
State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, FALSE )) {
|
|
|
|
printf("0x%x\n", State->State );
|
|
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Visit each transition state until we know the full paths
|
|
//
|
|
|
|
do {
|
|
|
|
Skipped = FALSE;
|
|
FoundState = FALSE;
|
|
Key = NULL;
|
|
|
|
for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key );
|
|
State != 0;
|
|
State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key )) {
|
|
|
|
// printf( "State: 0x%x\n", State->State );
|
|
|
|
//
|
|
// We don't know this state yet
|
|
//
|
|
|
|
if ((State->State != None) && IsListEmpty( &State->Links)) {
|
|
if (Verbose) {
|
|
printf( "skipping state 0x%x\n", State->State );
|
|
}
|
|
Skipped = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Look in the release only paths 1st
|
|
//
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTableRelease ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
if (OwnershipTransitionTableRelease[Index].Begin == State->State &&
|
|
OwnershipTransitionTableRelease[Index].End != None) {
|
|
|
|
StateOrder.State = OwnershipTransitionTableRelease[Index].End;
|
|
EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
|
|
|
|
//
|
|
// Is this a new state?
|
|
//
|
|
|
|
if (EndState && IsListEmpty( &EndState->Links )) {
|
|
|
|
FoundState = TRUE;
|
|
EndState->ReachedByRelease = TRUE;
|
|
|
|
if (Verbose) {
|
|
printf( "removing resource: 0x%x to state: 0x%x from 0x%x during release\n",
|
|
OwnershipTransitionTableRelease[Index].Acquired,
|
|
OwnershipTransitionTableRelease[Index].End,
|
|
State->State );
|
|
}
|
|
|
|
//
|
|
// Add the old state's resource except for the resource being released
|
|
//
|
|
|
|
if (State->State != None) {
|
|
Entry = (PRESOURCE_LIST)&State->Links;
|
|
do {
|
|
Entry = (PRESOURCE_LIST)Entry->Links.Flink;
|
|
|
|
if (Entry->Resource != OwnershipTransitionTableRelease[Index].Acquired) {
|
|
|
|
Entry2 = new RESOURCE_LIST;
|
|
Entry2->Resource = Entry->Resource;
|
|
|
|
InsertTailList( &EndState->Links, &Entry2->Links );
|
|
}
|
|
} while ( Entry->Links.Flink != &State->Links );
|
|
}
|
|
} // endif the new state is unknown
|
|
} // endif rule beginning matches the state
|
|
} // endfor over rules
|
|
|
|
//
|
|
// Then look in the acquire paths
|
|
//
|
|
|
|
NumTransitions = sizeof( OwnershipTransitionTable ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
|
for (Index=0; Index < NumTransitions; Index++) {
|
|
|
|
if (OwnershipTransitionTable[Index].Begin == State->State) {
|
|
|
|
StateOrder.State = OwnershipTransitionTable[Index].End;
|
|
EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
|
|
|
|
EndState->ReachedByRelease = FALSE;
|
|
|
|
//
|
|
// If we already know this state - then doublecheck this is an identical path
|
|
//
|
|
|
|
if (EndState && !IsListEmpty( &EndState->Links )) {
|
|
|
|
Entry = (PRESOURCE_LIST)EndState->Links.Flink;
|
|
if (Entry->Resource != NtfsResourceExVcb) {
|
|
Entry = (PRESOURCE_LIST)EndState->Links.Blink;
|
|
if (OwnershipTransitionTable[Index].Acquired != Entry->Resource ) {
|
|
printf( "2 paths to state: 0x%x 0x%x 0x%x\n", OwnershipTransitionTable[Index].End, Entry->Resource, OwnershipTransitionTable[Index].Acquired );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
FoundState = TRUE;
|
|
|
|
if (Verbose) {
|
|
printf( "adding resource: 0x%x to state: 0x%x from 0x%x\n",
|
|
OwnershipTransitionTable[Index].Acquired,
|
|
OwnershipTransitionTable[Index].End,
|
|
State->State );
|
|
}
|
|
|
|
//
|
|
// Add the old state's resource
|
|
//
|
|
|
|
if (State->State != None) {
|
|
Entry = (PRESOURCE_LIST)&State->Links;
|
|
do {
|
|
Entry = (PRESOURCE_LIST)Entry->Links.Flink;
|
|
|
|
Entry2 = new RESOURCE_LIST;
|
|
Entry2->Resource = Entry->Resource;
|
|
|
|
InsertTailList( &EndState->Links, &Entry2->Links );
|
|
|
|
} while ( Entry->Links.Flink != &State->Links );
|
|
}
|
|
|
|
//
|
|
// Finally add the transition resource into this state
|
|
//
|
|
|
|
Entry = new RESOURCE_LIST;
|
|
Entry->Resource = OwnershipTransitionTable[Index].Acquired;
|
|
|
|
InsertTailList( &EndState->Links, &Entry->Links );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Verbose) {
|
|
printf( "pass done\n" );
|
|
}
|
|
|
|
} while (FoundState && Skipped);
|
|
|
|
//
|
|
// Printout state maps
|
|
//
|
|
|
|
printf( "State Map\n" );
|
|
|
|
for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, TRUE );
|
|
State != 0;
|
|
State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, FALSE )) {
|
|
|
|
StateCheck = 0;
|
|
|
|
if (!IsListEmpty( &State->Links )) {
|
|
|
|
Entry = (PRESOURCE_LIST)&State->Links;
|
|
do {
|
|
Entry = (PRESOURCE_LIST)Entry->Links.Flink;
|
|
|
|
StateCheck |= Entry->Resource;
|
|
printf( "%x -> ", Entry->Resource );
|
|
|
|
} while ( Entry->Links.Flink != &State->Links );
|
|
printf( "state %x\n", State->State );
|
|
|
|
if ((int)State->State != StateCheck) {
|
|
printf( "State transistions do not make sense, check the state definition\n" );
|
|
}
|
|
|
|
} else {
|
|
printf( "unreachable state: 0x%x\n", State->State );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now build up individual transitions
|
|
//
|
|
|
|
printf( "Subtransitions\n" );
|
|
|
|
|
|
Key = NULL;
|
|
|
|
for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key );
|
|
State != 0;
|
|
State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key )) {
|
|
|
|
if (!IsListEmpty( &State->Links )) {
|
|
|
|
// printf( "State: 0x%x\n", State->State );
|
|
|
|
Entry = (PRESOURCE_LIST)&State->Links;
|
|
do {
|
|
Entry = (PRESOURCE_LIST)Entry->Links.Flink;
|
|
|
|
Entry2 = Entry;
|
|
while (Entry2->Links.Flink != &State->Links) {
|
|
BOOLEAN Found;
|
|
|
|
Entry2 = (PRESOURCE_LIST)Entry2->Links.Flink;
|
|
|
|
//
|
|
// First search if transition exists already from Entry->Resource to Entry2->Resource
|
|
//
|
|
|
|
Found = FALSE;
|
|
StateListOrderTemplate.Resource = Entry->Resource;
|
|
StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
|
|
|
|
if (!IsListEmpty( &StateListOrder->Links )) {
|
|
|
|
// printf( "FirstLink: 0x%x\n", StateListOrder->Links.Flink );
|
|
|
|
EntryState2 = StateListOrder;
|
|
do {
|
|
EntryState2 = CONTAINING_RECORD( EntryState2->Links.Flink, RESOURCE_STATE_LIST, Links );
|
|
if (EntryState2->Resource == Entry2->Resource) {
|
|
|
|
PRESOURCE_LIST OldEntry;
|
|
|
|
//
|
|
// Always choose state that started without vcb
|
|
//
|
|
|
|
StateOrder.State = EntryState2->EndState;
|
|
EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
|
|
|
|
OldEntry = (PRESOURCE_LIST)EndState->Links.Flink;
|
|
|
|
if ((OldEntry->Resource == NtfsResourceSharedVcb) ||
|
|
(OldEntry->Resource == NtfsResourceExVcb)) {
|
|
|
|
EntryState2->EndState = State->State;
|
|
}
|
|
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
} while ( EntryState2->Links.Flink != &StateListOrder->Links );
|
|
}
|
|
|
|
if (!Found) {
|
|
|
|
//
|
|
// Look for conflicts since its new
|
|
//
|
|
|
|
StateListOrderTemplate.Resource = Entry2->Resource;
|
|
StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
|
|
|
|
// printf( "Resource: 0x%x, empty: %d\n", StateListOrder->Resource, IsListEmpty( &StateListOrder->Links ) );
|
|
|
|
if (StateListOrder && !IsListEmpty( &StateListOrder->Links )) {
|
|
|
|
EntryState2 = StateListOrder;
|
|
|
|
do {
|
|
EntryState2 = CONTAINING_RECORD( EntryState2->Links.Flink, RESOURCE_STATE_LIST, Links );
|
|
|
|
if ((EntryState2->Resource == Entry->Resource)) {
|
|
printf( "possible conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
|
|
}
|
|
|
|
// printf( "check from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
|
|
|
|
StateOrder.State = EntryState2->EndState;
|
|
EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
|
|
|
|
StateOrder.State = State->State;
|
|
EndState2 = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
|
|
|
|
if ((EntryState2->Resource == Entry->Resource) &&
|
|
!EndState->ReachedByRelease &&
|
|
!EndState2->ReachedByRelease) {
|
|
|
|
//
|
|
// Now check if vcb locks it out
|
|
//
|
|
|
|
if (!(((((PRESOURCE_LIST)(EndState2->Links.Flink))->Resource == NtfsResourceSharedVcb) ||
|
|
((PRESOURCE_LIST)(EndState2->Links.Flink))->Resource == NtfsResourceExVcb) &&
|
|
|
|
((((PRESOURCE_LIST)(EndState->Links.Flink))->Resource == NtfsResourceSharedVcb) ||
|
|
((PRESOURCE_LIST)(EndState->Links.Flink))->Resource == NtfsResourceExVcb))) {
|
|
|
|
printf( "Suborder conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State->State, EntryState2->EndState );
|
|
break;
|
|
} else {
|
|
// printf( "NonSuborder conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
|
|
}
|
|
}
|
|
|
|
} while ( EntryState2->Links.Flink != &StateListOrder->Links );
|
|
} else {
|
|
// printf ("Unfound resource: 0x%x 0x%x\n", Entry2->Resource, StateListOrder );
|
|
}
|
|
|
|
StateListOrderTemplate.Resource = Entry->Resource;
|
|
StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
|
|
|
|
EntryState = new RESOURCE_STATE_LIST;
|
|
EntryState->Resource = Entry2->Resource;
|
|
EntryState->EndState = State->State;
|
|
|
|
// printf( "Adding 0x%x to list for resource 0x%x\n", EntryState, Entry->Resource );
|
|
|
|
InsertTailList( &StateListOrder->Links, &EntryState->Links );
|
|
}
|
|
}
|
|
|
|
} while ( Entry->Links.Flink != &State->Links );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dump the inidivual transitions
|
|
//
|
|
|
|
Key = NULL;
|
|
for (StateListOrder = (PRESOURCE_STATE_LIST) RtlEnumerateGenericTableWithoutSplaying( &ResourceTable, &Key );
|
|
StateListOrder != 0;
|
|
StateListOrder = (PRESOURCE_STATE_LIST) RtlEnumerateGenericTableWithoutSplaying( &ResourceTable, &Key )) {
|
|
|
|
if (!IsListEmpty( &StateListOrder->Links )) {
|
|
EntryState = StateListOrder;
|
|
do {
|
|
EntryState = CONTAINING_RECORD( EntryState->Links.Flink, RESOURCE_STATE_LIST, Links );
|
|
printf( "0x%x -> 0x%x endstate:0x%x\n ", StateListOrder->Resource, EntryState->Resource, EntryState->EndState );
|
|
|
|
} while ( EntryState->Links.Flink != &StateListOrder->Links );
|
|
}
|
|
}
|
|
|
|
} // main
|