You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
747 lines
20 KiB
747 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tree.c
|
|
|
|
Abstract:
|
|
|
|
Cluster resource tree management routines.
|
|
|
|
Author:
|
|
|
|
Rod Gamache (rodga) 17-Apr-1996
|
|
|
|
|
|
Notes:
|
|
|
|
WARNING: All of the routines in this file assume that the resource
|
|
lock is held when they are called.
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "fmp.h"
|
|
|
|
|
|
#define LOG_MODULE TREE
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
|
|
//
|
|
// Local function prototypes
|
|
//
|
|
BOOL
|
|
FmpAddResourceToDependencyTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN PFM_DEPENDENCY_TREE Tree
|
|
);
|
|
|
|
BOOL
|
|
FmpIsResourceInDependencyTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN PFM_DEPENDENCY_TREE Tree
|
|
);
|
|
|
|
DWORD
|
|
FmpOfflineWaitingResourceTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN BOOL BringQuorumOffline
|
|
);
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
FmpRestartResourceTree(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine brings back part of a dependency tree, starting from the
|
|
point of the last failure.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource object that last failed and is
|
|
restarting.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - if the request is successful.
|
|
A Win32 error if the request fails.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PDEPENDENCY dependency;
|
|
DWORD status;
|
|
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//
|
|
// Tell the resource monitor to restart this resource if needed.
|
|
//
|
|
|
|
//
|
|
// If the current state is not online and we want it to be online, then
|
|
// bring it online.
|
|
//
|
|
|
|
if ( (Resource->State != ClusterResourceOnline) &&
|
|
((Resource->PersistentState == ClusterResourceOnline)) ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RestartResourceTree, Restart resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
status = FmpOnlineResource(Resource, FALSE);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If this resource has any dependents, start them if needed.
|
|
//
|
|
for ( entry = Resource->ProvidesFor.Flink;
|
|
entry != &(Resource->ProvidesFor);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
|
|
|
//
|
|
// Recursively restart the dependent resource.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] RestartResourceTree, %1!ws! depends on %2!ws!. Restart first\n",
|
|
OmObjectId(dependency->DependentResource),
|
|
OmObjectId(Resource));
|
|
|
|
status = FmpRestartResourceTree(dependency->DependentResource);
|
|
}
|
|
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // FmpRestartResourceTree
|
|
|
|
|
|
|
|
DWORD
|
|
FmpOnlineWaitingTree(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine brings back part of a dependency tree, starting from the
|
|
point of the last waiting resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource object that is now online.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - if the request is successful.
|
|
A Win32 error if the request fails.
|
|
|
|
Notes:
|
|
|
|
This routine is only called when the given resource is online.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PDEPENDENCY dependency;
|
|
DWORD status;
|
|
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//if shutdown is in progress, dont bring resources online
|
|
if (FmpShutdown)
|
|
{
|
|
//
|
|
// If this resource has any dependents, and they are in online pending state
|
|
// mark them as offline.
|
|
//
|
|
for ( entry = Resource->ProvidesFor.Flink;
|
|
entry != &(Resource->ProvidesFor);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
|
|
|
if ((dependency->DependentResource->State == ClusterResourceOnlinePending) &&
|
|
(dependency->DependentResource->Flags & RESOURCE_WAITING))
|
|
{
|
|
//set the state of the all dependent resources to be offline again
|
|
FmpPropagateResourceState(dependency->DependentResource, ClusterResourceOffline);
|
|
//set the resource to be not waiting
|
|
dependency->DependentResource->Flags &= ~RESOURCE_WAITING;
|
|
|
|
//
|
|
// Recursively set the state of all dependent resources to offline
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OnlineWaitingTree, %1!ws! (%2!u!) depends on %3!ws! (%4!u!). Shutdown others\n",
|
|
OmObjectId(dependency->DependentResource),
|
|
dependency->DependentResource->State,
|
|
OmObjectId(Resource),
|
|
Resource->State);
|
|
|
|
status = FmpOnlineWaitingTree(dependency->DependentResource);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 11/5/1999
|
|
//
|
|
// Ensure that the resource state itself is made
|
|
// ClusterResourceOffline if FM is asked to shutdown. Note that
|
|
// this function is recursively called from below, not just from
|
|
// the FM worker thread. So, if FM happened to be shutdown
|
|
// while executing this function called from below, then we
|
|
// offline all the dependent resources above, but not the
|
|
// resource itself. This is done here.
|
|
//
|
|
if ( ( Resource->State == ClusterResourceOnlinePending ) &&
|
|
( Resource->Flags & RESOURCE_WAITING ) )
|
|
{
|
|
FmpPropagateResourceState( Resource, ClusterResourceOffline );
|
|
|
|
Resource->Flags &= ~RESOURCE_WAITING;
|
|
|
|
ClRtlLogPrint( LOG_NOISE,
|
|
"[FM] OnlineWaitingTree, Resource <%1!ws!> forcibly brought offline...\n",
|
|
OmObjectId( Resource ) );
|
|
}
|
|
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
//for normal-non shutdown case
|
|
//
|
|
// Tell the resource monitor to restart this resource if needed.
|
|
//
|
|
|
|
//
|
|
// If the current state is not online and it is waiting, then it probably
|
|
// needs to be brought online now.
|
|
//
|
|
|
|
if ( (Resource->State == ClusterResourceOnlinePending) &&
|
|
(Resource->Flags & RESOURCE_WAITING) ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOnlineWaitingTree, Start resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
Resource->State = ClusterResourceOffline;
|
|
status = FmpOnlineResource(Resource, FALSE);
|
|
if ( status == ERROR_SUCCESS ) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOnlineWaitingTree, online for resource %1!ws! succeeded, online the dependents\r\n",
|
|
OmObjectId(Resource));
|
|
}
|
|
else if (status == ERROR_QUORUM_RESOURCE_ONLINE_FAILED)
|
|
{
|
|
PRESOURCE_ENUM pResourceEnum;
|
|
LPWSTR pszNewId;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOnlineWaitingTree, online for resource %1!ws!, status = %2!u!.\n",
|
|
OmObjectId(Resource),
|
|
status);
|
|
|
|
pResourceEnum = (PRESOURCE_ENUM)LocalAlloc(LMEM_FIXED,
|
|
sizeof(RESOURCE_ENUM));
|
|
if (!pResourceEnum)
|
|
{
|
|
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|
CsInconsistencyHalt(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
pResourceEnum->EntryCount = 1;
|
|
pResourceEnum->ContainsQuorum = (Resource == gpQuoResource);
|
|
pszNewId = LocalAlloc(LMEM_FIXED, (lstrlenW(OmObjectId(Resource))+1) * sizeof(WCHAR));
|
|
if ( pszNewId == NULL )
|
|
{
|
|
CsInconsistencyHalt(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
lstrcpyW(pszNewId, OmObjectId(Resource));
|
|
pResourceEnum->Entry[0].Id = pszNewId;
|
|
pResourceEnum->Entry[0].State = Resource->PersistentState;
|
|
FmpSubmitRetryOnline(pResourceEnum, NULL);
|
|
FmpReleaseLocalResourceLock(Resource);
|
|
|
|
LocalFree(pszNewId);
|
|
LocalFree(pResourceEnum);
|
|
return(status);
|
|
}
|
|
else
|
|
{
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOnlineWaitingTree, online for resource %1!ws! returned = %2!u!.\n",
|
|
OmObjectId(Resource),
|
|
status);
|
|
return(status);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this resource has any dependents, start them if needed.
|
|
//
|
|
for ( entry = Resource->ProvidesFor.Flink;
|
|
entry != &(Resource->ProvidesFor);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
|
|
|
|
//
|
|
// Recursively restart the dependent resource.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OnlineWaitingTree, %1!ws! (%2!u!) depends on %3!ws! (%4!u!). Start now\n",
|
|
OmObjectId(dependency->DependentResource),
|
|
dependency->DependentResource->State,
|
|
OmObjectId(Resource),
|
|
Resource->State);
|
|
|
|
status = FmpOnlineWaitingTree(dependency->DependentResource);
|
|
|
|
}
|
|
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // FmpOnlineWaitingTree
|
|
|
|
|
|
DWORD
|
|
FmpOfflineWaitingTree(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PDEPENDENCY dependency;
|
|
DWORD status;
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOfflineWaitingTree: Entry for <%1!ws!>.\n",
|
|
OmObjectName( Resource ) );
|
|
|
|
//
|
|
// Tell the resource monitor to stop this resource if needed.
|
|
// Make sure that the quorum resource is the last one brought offline
|
|
//
|
|
status = FmpOfflineWaitingResourceTree(Resource, FALSE);
|
|
|
|
//the quorum resource might still need to come offline, if it is in this group
|
|
if ((status == ERROR_SUCCESS) && (Resource->Group == gpQuoResource->Group))
|
|
{
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOfflineWaitingTree: Quorum resource is in the same group,Moving list=0x%1!08lx!\n",
|
|
Resource->Group->MovingList);
|
|
|
|
|
|
//if a move is pending bring the quorum resource offline if all resources
|
|
// in the group are offline
|
|
// else dont bring the quorum resource offline
|
|
// this is because we dont bring the quorum resource offline on group offlines
|
|
if (Resource->Group->MovingList)
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
DWORD BringQuorumOffline = TRUE;
|
|
PFM_RESOURCE pGroupResource;
|
|
|
|
|
|
for ( listEntry = Resource->Group->Contains.Flink;
|
|
listEntry != &(Resource->Group->Contains);
|
|
listEntry = listEntry->Flink )
|
|
{
|
|
pGroupResource = CONTAINING_RECORD(listEntry,
|
|
FM_RESOURCE,
|
|
ContainsLinkage );
|
|
|
|
// if this is the quorum resource continue
|
|
if (pGroupResource->QuorumResource)
|
|
continue;
|
|
//if the state is not offline or failed, dont try
|
|
//and bring the quorum resource offline
|
|
if ((pGroupResource->State != ClusterResourceOffline) &&
|
|
(pGroupResource->State != ClusterResourceFailed))
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOfflineWaitingTree: Quorum cannot be brought offline now for <%1!ws!>, state=%2!u!\n",
|
|
OmObjectName(pGroupResource), pGroupResource->State);
|
|
|
|
BringQuorumOffline = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (BringQuorumOffline)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOfflineWaitingTree: bring quorum resource offline\n");
|
|
status = FmpOfflineResource(gpQuoResource, FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpOfflineWaitingTree: returned status %1!u! for <%2!ws!>.\n",
|
|
status, OmObjectName( Resource ) );
|
|
return(status);
|
|
|
|
}
|
|
|
|
DWORD
|
|
FmpOfflineWaitingResourceTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN BOOL BringQuorumOffline
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine offlines a dependency tree, starting from the
|
|
point of the last waiting resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource object that is now offline.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS - if the request is successful.
|
|
A Win32 error if the request fails.
|
|
|
|
Notes:
|
|
|
|
This routine is only called when the given resource is offline.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY entry;
|
|
PDEPENDENCY dependency;
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
|
|
FmpAcquireLocalResourceLock( Resource );
|
|
|
|
//
|
|
// Tell the resource monitor to stop this resource if needed.
|
|
//
|
|
|
|
//
|
|
// If the current state is not offline and it is waiting, then it probably
|
|
// needs to be brought offline now.
|
|
//
|
|
|
|
if ((Resource->State == ClusterResourceOfflinePending) &&
|
|
(Resource->Flags & RESOURCE_WAITING)) {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OfflineWaitingResourceTree, Offline resource %1!ws!\n",
|
|
OmObjectId(Resource));
|
|
Resource->State = ClusterResourceOnline;
|
|
status = FmpOfflineResource(Resource, FALSE);
|
|
if ( status == ERROR_IO_PENDING ) {
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OfflineWaitingResourceTree, offline for resource %1!ws! returned pending.\n",
|
|
OmObjectId(Resource));
|
|
return(status);
|
|
} else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OfflineWaitingResourceTree, offline for resource %1!ws!, status = %2!u!.\n",
|
|
OmObjectId(Resource),
|
|
status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this resource has any providers, stop them if needed.
|
|
//
|
|
for ( entry = Resource->DependsOn.Flink;
|
|
entry != &(Resource->DependsOn);
|
|
entry = entry->Flink
|
|
)
|
|
{
|
|
dependency = CONTAINING_RECORD(entry, DEPENDENCY, DependentLinkage);
|
|
|
|
if (dependency->ProviderResource->QuorumResource && !BringQuorumOffline)
|
|
{
|
|
continue;
|
|
}
|
|
//
|
|
// Recursively offline the provider resource.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OfflineWaitingResourceTree, %1!ws! provides for %2!ws!. Offline next.\n",
|
|
OmObjectId(dependency->ProviderResource),
|
|
OmObjectId(Resource));
|
|
|
|
//dependency->ProviderResource->Flags |= RESOURCE_WAITING;
|
|
status = FmpOfflineWaitingResourceTree(dependency->ProviderResource, BringQuorumOffline);
|
|
|
|
}
|
|
|
|
FmpReleaseLocalResourceLock( Resource );
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] OfflineWaitingResourceTree: Exit, status=%1!u! for <%2!ws!>.\n",
|
|
status, OmObjectName( Resource ) );
|
|
return(status);
|
|
|
|
} // FmpOfflineWaitingResourceTree
|
|
|
|
|
|
PFM_DEPENDENCY_TREE
|
|
FmCreateFullDependencyTree(
|
|
IN PFM_RESOURCE Resource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a full dependency tree containing all the resources
|
|
that either depend on or provide for the supplied resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource
|
|
|
|
Return Value:
|
|
|
|
Pointer to the dependency tree.
|
|
|
|
NULL if out of memory.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_DEPENDENCY_TREE Tree;
|
|
BOOL Success;
|
|
|
|
Tree = LocalAlloc(LMEM_FIXED, sizeof(FM_DEPENDENCY_TREE));
|
|
if (Tree == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(NULL);
|
|
}
|
|
InitializeListHead(&Tree->ListHead);
|
|
|
|
//
|
|
// Add the resources that the specified resource depends on.
|
|
//
|
|
Success = FmpAddResourceToDependencyTree(Resource, Tree);
|
|
if (!Success) {
|
|
LocalFree(Tree);
|
|
return(NULL);
|
|
} else {
|
|
return(Tree);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
FmpIsResourceInDependencyTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN PFM_DEPENDENCY_TREE Tree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether the specified resource is already in the
|
|
dependency tree.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to check for
|
|
|
|
Tree - Supplies the dependency tree.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the resource is in the dependency tree
|
|
|
|
FALSE if the resource is not in the dependency tree
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PFM_DEPENDTREE_ENTRY Node;
|
|
|
|
ListEntry = Tree->ListHead.Flink;
|
|
while (ListEntry != &Tree->ListHead) {
|
|
Node = CONTAINING_RECORD(ListEntry,
|
|
FM_DEPENDTREE_ENTRY,
|
|
ListEntry);
|
|
if (Node->Resource == Resource) {
|
|
return(TRUE);
|
|
}
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FmpAddResourceToDependencyTree(
|
|
IN PFM_RESOURCE Resource,
|
|
IN PFM_DEPENDENCY_TREE Tree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Recursive worker for adding a resource and all resources that
|
|
it depends on or provides for into the dependency tree.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the resource to add.
|
|
|
|
Tree - Supplies the tree the resource should be added to.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Successfully completed
|
|
|
|
FALSE - out of memory
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PDEPENDENCY Dependency;
|
|
PFM_DEPENDTREE_ENTRY Node;
|
|
|
|
//
|
|
// First check to see if we are already in the tree.
|
|
// If so, we are done.
|
|
//
|
|
if (FmpIsResourceInDependencyTree(Resource, Tree)) {
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//
|
|
// Recursively call ourselves for each entry we depend on.
|
|
//
|
|
ListEntry = Resource->DependsOn.Flink;
|
|
while (ListEntry != &Resource->DependsOn) {
|
|
Dependency = CONTAINING_RECORD(ListEntry,
|
|
DEPENDENCY,
|
|
DependentLinkage);
|
|
ListEntry = ListEntry->Flink;
|
|
//
|
|
// Recursively add this resource to the tree
|
|
//
|
|
if (!FmpAddResourceToDependencyTree(Dependency->ProviderResource, Tree)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add ourselves to the list now if we are not already in it.
|
|
//
|
|
if (!FmpIsResourceInDependencyTree(Resource, Tree)) {
|
|
//
|
|
// Add ourselves to the end of the list.
|
|
//
|
|
Node = LocalAlloc(LMEM_FIXED, sizeof(FM_DEPENDTREE_ENTRY));
|
|
if (Node == NULL) {
|
|
return(FALSE);
|
|
}
|
|
OmReferenceObject(Resource);
|
|
Node->Resource = Resource;
|
|
InsertTailList(&Tree->ListHead, &Node->ListEntry);
|
|
}
|
|
|
|
|
|
//
|
|
// Now add the resources that this resource provides for to the list.
|
|
//
|
|
ListEntry = Resource->ProvidesFor.Flink;
|
|
while (ListEntry != &Resource->ProvidesFor) {
|
|
Dependency = CONTAINING_RECORD(ListEntry,
|
|
DEPENDENCY,
|
|
ProviderLinkage);
|
|
ListEntry = ListEntry->Flink;
|
|
//
|
|
// Recursively add this resource to the tree
|
|
//
|
|
if (!FmpAddResourceToDependencyTree(Dependency->DependentResource, Tree)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
VOID
|
|
FmDestroyFullDependencyTree(
|
|
IN PFM_DEPENDENCY_TREE Tree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys a dependency tree
|
|
|
|
Arguments:
|
|
|
|
Tree - Supplies the dependency tree
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_DEPENDTREE_ENTRY Entry;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
while (!IsListEmpty(&Tree->ListHead)) {
|
|
ListEntry = RemoveHeadList(&Tree->ListHead);
|
|
Entry = CONTAINING_RECORD(ListEntry,
|
|
FM_DEPENDTREE_ENTRY,
|
|
ListEntry);
|
|
OmDereferenceObject(Entry->Resource);
|
|
LocalFree(Entry);
|
|
}
|
|
LocalFree(Tree);
|
|
}
|