766 lines
15 KiB
C
766 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
node.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the Appletalk Node management code.
|
||
|
||
Author:
|
||
|
||
Jameel Hyder (jameelh@microsoft.com)
|
||
Nikhil Kamkolkar (nikhilk@microsoft.com)
|
||
|
||
Revision History:
|
||
19 Jun 1992 Initial Version
|
||
|
||
Notes: Tab stop: 4
|
||
--*/
|
||
|
||
#include <atalk.h>
|
||
#pragma hdrstop
|
||
#define FILENUM NODE
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEINIT, AtalkInitNodeCreateOnPort)
|
||
#pragma alloc_text(PAGEINIT, AtalkInitNodeAllocate)
|
||
#pragma alloc_text(PAGEINIT, AtalkInitNodeGetPramAddr)
|
||
#pragma alloc_text(PAGEINIT, AtalkInitNodeSavePramAddr)
|
||
#endif
|
||
|
||
ATALK_ERROR
|
||
AtalkInitNodeCreateOnPort(
|
||
PPORT_DESCRIPTOR pPortDesc,
|
||
BOOLEAN AllowStartupRange,
|
||
BOOLEAN RouterNode,
|
||
PATALK_NODEADDR NodeAddr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PATALK_NODE pNode;
|
||
ATALK_ERROR error = ATALK_NO_ERROR;
|
||
ATALK_NODEADDR desiredNode = { UNKNOWN_NETWORK, UNKNOWN_NODE};
|
||
PWSTR NodeName;
|
||
KIRQL OldIrql;
|
||
|
||
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
|
||
|
||
do
|
||
{
|
||
if ((pPortDesc->pd_Flags & PD_FINDING_NODE) == 0)
|
||
{
|
||
pPortDesc->pd_Flags |= PD_FINDING_NODE;
|
||
}
|
||
else
|
||
{
|
||
// Return if we are already trying to find a node
|
||
error = ATALK_NODE_FINDING;
|
||
break;
|
||
}
|
||
|
||
// We should not be here if we have already allocated a router node and the user nodes
|
||
ASSERT(!RouterNode || ((pPortDesc->pd_Flags & PD_ROUTER_NODE) == 0));
|
||
ASSERT ((pPortDesc->pd_Flags & (PD_ROUTER_NODE | PD_USER_NODE_1 | PD_USER_NODE_2))
|
||
!= (PD_ROUTER_NODE | PD_USER_NODE_1 | PD_USER_NODE_2));
|
||
|
||
// On non-extended ports we only allow one node! The theory being that some
|
||
// LocalTalk cards are too smart for their own good and have a concept of
|
||
// their "source node number" and thus only support one node, also on
|
||
// non-extended ports, nodes are scarse.
|
||
if (!EXT_NET(pPortDesc))
|
||
{
|
||
// For a localtalk node we do things differently.
|
||
// During initialization time, we would have obtained
|
||
// the address from the mac, that will be the node
|
||
// address.
|
||
|
||
ASSERT(pPortDesc->pd_Flags & PD_BOUND);
|
||
ASSERT(pPortDesc->pd_AlapNode != 0);
|
||
|
||
// This needs to be initialized to UNKNOWN_NETWORK or obtained
|
||
// from the net during initialization.
|
||
ASSERT(pPortDesc->pd_NetworkRange.anr_FirstNetwork == UNKNOWN_NETWORK);
|
||
|
||
if (!ATALK_SUCCESS((error = AtalkInitNodeAllocate(pPortDesc, &pNode))))
|
||
{
|
||
LOG_ERRORONPORT(pPortDesc,
|
||
EVENT_ATALK_INIT_COULDNOTGETNODE,
|
||
0,
|
||
NULL,
|
||
0);
|
||
break;
|
||
}
|
||
|
||
// Use the allocated structure to set the info.
|
||
// Thread this into the port structure.
|
||
pPortDesc->pd_LtNetwork =
|
||
pNode->an_NodeAddr.atn_Network =
|
||
pPortDesc->pd_NetworkRange.anr_FirstNetwork;
|
||
pNode->an_NodeAddr.atn_Node = (BYTE)pPortDesc->pd_AlapNode;
|
||
|
||
// Reference the port for this node.
|
||
AtalkPortReferenceByPtrNonInterlock(pPortDesc, &error);
|
||
if (!ATALK_SUCCESS(error))
|
||
{
|
||
AtalkFreeMemory(pNode);
|
||
break;
|
||
}
|
||
|
||
// Now put it in the port descriptor
|
||
pNode->an_Next = pPortDesc->pd_Nodes;
|
||
pPortDesc->pd_Nodes = pNode;
|
||
}
|
||
else
|
||
{
|
||
// Use PRAM values if we have them
|
||
if (RouterNode)
|
||
{
|
||
NodeName = ROUTER_NODE_VALUE;
|
||
if (pPortDesc->pd_RoutersPramNode.atn_Network != UNKNOWN_NETWORK)
|
||
{
|
||
desiredNode = pPortDesc->pd_RoutersPramNode;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ((pPortDesc->pd_Flags & PD_USER_NODE_1) == 0)
|
||
{
|
||
NodeName = USER_NODE1_VALUE;
|
||
if (pPortDesc->pd_UsersPramNode1.atn_Network != UNKNOWN_NETWORK)
|
||
{
|
||
// If we are not a router node, and the first user node
|
||
// has not been allocated...
|
||
desiredNode = pPortDesc->pd_UsersPramNode1;
|
||
}
|
||
}
|
||
else if ((pPortDesc->pd_Flags & PD_USER_NODE_2) == 0)
|
||
{
|
||
NodeName = USER_NODE2_VALUE;
|
||
if (pPortDesc->pd_UsersPramNode2.atn_Network != UNKNOWN_NETWORK)
|
||
{
|
||
// If we are not a router node, and the second user node
|
||
// has not been allocated...
|
||
desiredNode = pPortDesc->pd_UsersPramNode2;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Flags should be set so future get node requests return failure
|
||
// until we are done with this attempt. We need to call
|
||
// the aarp routines without the lock held - they will
|
||
// block.
|
||
|
||
ASSERT(pPortDesc->pd_Flags & PD_FINDING_NODE);
|
||
|
||
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
|
||
|
||
// If this routine succeeds in finding the node, it
|
||
// will chain in the atalkNode into the port. It also
|
||
// returns with the proper flags set/reset in the
|
||
// pPortDesc structure. It will also have referenced the port
|
||
// and inserted the node into the port's node list.
|
||
error = AtalkInitAarpForNodeOnPort(pPortDesc,
|
||
AllowStartupRange,
|
||
desiredNode,
|
||
&pNode);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
|
||
|
||
if (!ATALK_SUCCESS(error))
|
||
{
|
||
// AARP for node failed.
|
||
LOG_ERRORONPORT(pPortDesc,
|
||
EVENT_ATALK_INIT_COULDNOTGETNODE,
|
||
0,
|
||
NULL,
|
||
0);
|
||
}
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
// Ok, done finding node. No need for a crit section.
|
||
pPortDesc->pd_Flags &= ~PD_FINDING_NODE;
|
||
|
||
if (ATALK_SUCCESS(error))
|
||
{
|
||
// If router node, remember it in port descriptor
|
||
// Do this before setting up the rtmp/nbp listeners.
|
||
// In anycase, clients must check this value for null,
|
||
// not guaranteed as zip socket could already be open.
|
||
if (RouterNode)
|
||
pPortDesc->pd_RouterNode = pNode;
|
||
|
||
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
|
||
|
||
// Setup the RTMP, NBP and EP listeners on this node.
|
||
// These will be the non-router versions. StartRouting
|
||
// calls will then switch them to be the router versions
|
||
// at the appropriate time.
|
||
|
||
error = AtalkInitDdpOpenStaticSockets(pPortDesc, pNode);
|
||
|
||
if (ATALK_SUCCESS(error))
|
||
{
|
||
if (EXT_NET(pPortDesc))
|
||
{
|
||
// We always save this address.
|
||
AtalkInitNodeSavePramAddr(pPortDesc,
|
||
NodeName,
|
||
&pNode->an_NodeAddr);
|
||
}
|
||
|
||
// Return the address of the node opened.
|
||
if (NodeAddr != NULL)
|
||
*NodeAddr = pNode->an_NodeAddr;
|
||
}
|
||
else
|
||
{
|
||
// Error opening sockets. Release node, return failure
|
||
LOG_ERRORONPORT(pPortDesc,
|
||
EVENT_ATALK_NODE_OPENSOCKETS,
|
||
0,
|
||
NULL,
|
||
0);
|
||
AtalkNodeReleaseOnPort(pPortDesc, pNode);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
|
||
}
|
||
|
||
if (!ATALK_SUCCESS(error))
|
||
{
|
||
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
|
||
("Creation node on port %lx failed! %lx\n",
|
||
pPortDesc, error));
|
||
}
|
||
else
|
||
{
|
||
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
|
||
("Creation node on port %lx with addr %lx.%lx and p%lx\n",
|
||
pPortDesc, pNode->an_NodeAddr.atn_Network,
|
||
pNode->an_NodeAddr.atn_Node, pNode));
|
||
}
|
||
|
||
return(error);
|
||
}
|
||
|
||
|
||
|
||
|
||
ATALK_ERROR
|
||
AtalkNodeReleaseOnPort(
|
||
PPORT_DESCRIPTOR pPortDesc,
|
||
PATALK_NODE pNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PDDP_ADDROBJ pDdpAddr, pNextAddr;
|
||
ATALK_ERROR error;
|
||
KIRQL OldIrql;
|
||
SHORT i;
|
||
|
||
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_WARN,
|
||
("AtalkNodeReleaseOnPort: Releasing node %lx on port %lx!\n", pNode, pPortDesc));
|
||
|
||
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
|
||
|
||
if ((pNode->an_NodeAddr.atn_Network == AtalkUserNode1.atn_Network) &&
|
||
(pNode->an_NodeAddr.atn_Node == AtalkUserNode1.atn_Node))
|
||
{
|
||
pPortDesc->pd_Flags &= ~PD_USER_NODE_1;
|
||
AtalkUserNode1.atn_Network = 0;
|
||
AtalkUserNode1.atn_Node = 0;
|
||
}
|
||
else if ((pNode->an_NodeAddr.atn_Network == AtalkUserNode2.atn_Network) &&
|
||
(pNode->an_NodeAddr.atn_Node == AtalkUserNode2.atn_Node))
|
||
{
|
||
pPortDesc->pd_Flags &= ~PD_USER_NODE_2;
|
||
AtalkUserNode2.atn_Network = 0;
|
||
AtalkUserNode2.atn_Node = 0;
|
||
}
|
||
|
||
if ((pNode->an_Flags & AN_CLOSING) == 0)
|
||
{
|
||
// Set the closing flag.
|
||
pNode->an_Flags |= AN_CLOSING;
|
||
|
||
// First close all the sockets on the node
|
||
for (i = 0; i < NODE_DDPAO_HASH_SIZE; i++)
|
||
{
|
||
pNextAddr = NULL;
|
||
AtalkDdpReferenceNextNc(pNode->an_DdpAoHash[i],
|
||
&pDdpAddr,
|
||
&error);
|
||
|
||
if (!ATALK_SUCCESS(error))
|
||
{
|
||
// Check the other hash table entries. No non-closing
|
||
// sockets on this list.
|
||
continue;
|
||
}
|
||
|
||
while (TRUE)
|
||
{
|
||
// Get the next non-closing node using our referenced node before
|
||
// closing it. Note we use pDdpAddr->...Flink.
|
||
AtalkDdpReferenceNextNc(pDdpAddr->ddpao_Next,
|
||
&pNextAddr,
|
||
&error);
|
||
|
||
// Close the referenced ddp addr after releasing the node lock.
|
||
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
|
||
|
||
if (pDdpAddr->ddpao_Flags & DDPAO_SOCK_INTERNAL)
|
||
{
|
||
AtalkDdpCloseAddress(pDdpAddr, NULL, NULL);
|
||
}
|
||
else
|
||
{
|
||
AtalkDdpPnPSuspendAddress(pDdpAddr);
|
||
}
|
||
|
||
// Dereference the address.
|
||
AtalkDdpDereference(pDdpAddr);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
|
||
|
||
if (pNextAddr != NULL)
|
||
pDdpAddr = pNextAddr;
|
||
else
|
||
break;
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
|
||
|
||
// Remove the creation reference for this node.
|
||
AtalkNodeDereference(pNode);
|
||
}
|
||
else
|
||
{
|
||
// We are already closing.
|
||
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
|
||
}
|
||
|
||
return(ATALK_NO_ERROR);
|
||
}
|
||
|
||
|
||
|
||
|
||
BOOLEAN
|
||
AtalkNodeExistsOnPort(
|
||
PPORT_DESCRIPTOR pPortDesc,
|
||
PATALK_NODEADDR pNodeAddr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PATALK_NODE pCheckNode;
|
||
BOOLEAN exists = FALSE;
|
||
|
||
|
||
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
|
||
|
||
for (pCheckNode = pPortDesc->pd_Nodes;
|
||
pCheckNode != NULL;
|
||
pCheckNode = pCheckNode->an_Next)
|
||
{
|
||
if (ATALK_NODES_EQUAL(&pCheckNode->an_NodeAddr, pNodeAddr))
|
||
{
|
||
exists = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
|
||
|
||
return(exists);
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkInitNodeSavePramAddr(
|
||
IN PPORT_DESCRIPTOR pPortDesc,
|
||
IN PWSTR RegValue,
|
||
OUT PATALK_NODEADDR pNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
UNICODE_STRING valueName;
|
||
ULONG bytesWritten;
|
||
ULONG ValueToSave;
|
||
|
||
// Save the node value as xxxx00yy where xxxx is network, yy is node
|
||
ValueToSave = (pNode->atn_Network << 16) + pNode->atn_Node;
|
||
|
||
RtlInitUnicodeString (&valueName, RegValue);
|
||
|
||
ZwSetValueKey(pPortDesc->pd_AdapterInfoHandle,
|
||
&valueName,
|
||
0,
|
||
REG_DWORD,
|
||
&ValueToSave,
|
||
sizeof(ULONG));
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkInitNodeGetPramAddr(
|
||
IN PPORT_DESCRIPTOR pPortDesc,
|
||
IN PWSTR RegValue,
|
||
OUT PATALK_NODEADDR pNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING valueName;
|
||
ULONG bytesWritten;
|
||
ULONG ValueRead;
|
||
BYTE buffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 32];
|
||
PKEY_VALUE_FULL_INFORMATION nodeValue = (PKEY_VALUE_FULL_INFORMATION)buffer;
|
||
|
||
RtlInitUnicodeString (&valueName, RegValue);
|
||
|
||
Status = ZwQueryValueKey(pPortDesc->pd_AdapterInfoHandle,
|
||
&valueName,
|
||
KeyValueFullInformation,
|
||
buffer,
|
||
sizeof(buffer),
|
||
&bytesWritten);
|
||
if (NT_SUCCESS(Status))
|
||
{
|
||
ValueRead = *(PULONG)(buffer + nodeValue->DataOffset);
|
||
}
|
||
else
|
||
{
|
||
ValueRead = 0;
|
||
ASSERT (UNKNOWN_NETWORK == 0);
|
||
ASSERT (UNKNOWN_NODE == 0);
|
||
}
|
||
pNode->atn_Node = (BYTE)(ValueRead & 0xFF);
|
||
pNode->atn_Network = (USHORT)(ValueRead >> 16);
|
||
if ((pNode->atn_Network == UNKNOWN_NETWORK) ||
|
||
(pNode->atn_Node == UNKNOWN_NODE))
|
||
{
|
||
pNode->atn_Node = UNKNOWN_NODE;
|
||
pNode->atn_Network = UNKNOWN_NETWORK;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkZapPramValue(
|
||
IN PPORT_DESCRIPTOR pPortDesc,
|
||
IN PWSTR RegValue
|
||
)
|
||
{
|
||
UNICODE_STRING valueName;
|
||
ULONG bytesWritten;
|
||
ULONG ValueToSave;
|
||
|
||
// Write 0 to the value to zap it for now.
|
||
ValueToSave = 0;
|
||
|
||
RtlInitUnicodeString (&valueName, RegValue);
|
||
|
||
ZwSetValueKey(pPortDesc->pd_AdapterInfoHandle,
|
||
&valueName,
|
||
0,
|
||
REG_DWORD,
|
||
&ValueToSave,
|
||
sizeof(ULONG));
|
||
}
|
||
|
||
|
||
ATALK_ERROR
|
||
AtalkInitNodeAllocate(
|
||
IN PPORT_DESCRIPTOR pPortDesc,
|
||
OUT PATALK_NODE *ppNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PATALK_NODE pNode;
|
||
|
||
// Allocate a new active Node structure
|
||
if ((pNode = (PATALK_NODE)AtalkAllocZeroedMemory(sizeof(ATALK_NODE))) == NULL)
|
||
{
|
||
return(ATALK_RESR_MEM);
|
||
}
|
||
|
||
// Initialize some elements of the structure. Remaining stuff
|
||
// done when the node is actually being inserted into the port
|
||
// hash table.
|
||
#if DBG
|
||
pNode->an_Signature = AN_SIGNATURE;
|
||
#endif
|
||
|
||
// Initialize the Nbp Id & Enumerator
|
||
pNode->an_NextNbpId = 0;
|
||
pNode->an_NextNbpEnum = 0;
|
||
pNode->an_NextDynSkt = FIRST_DYNAMIC_SOCKET;
|
||
INITIALIZE_SPIN_LOCK(&pNode->an_Lock);
|
||
pNode->an_Port = pPortDesc; // Port on which node exists
|
||
pNode->an_RefCount = 1; // Reference for creation.
|
||
|
||
// Return pointer to allocated node
|
||
*ppNode = pNode;
|
||
|
||
return(ATALK_NO_ERROR);
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkNodeRefByAddr(
|
||
IN PPORT_DESCRIPTOR pPortDesc,
|
||
IN PATALK_NODEADDR NodeAddr,
|
||
OUT PATALK_NODE * ppNode,
|
||
OUT PATALK_ERROR pErr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PATALK_NODE pNode;
|
||
KIRQL OldIrql;
|
||
BOOLEAN foundNode = FALSE;
|
||
|
||
*pErr = ATALK_NODE_NONEXISTENT;
|
||
|
||
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
|
||
for (pNode = pPortDesc->pd_Nodes; pNode != NULL; pNode = pNode->an_Next)
|
||
{
|
||
ASSERT(VALID_ATALK_NODE(pNode));
|
||
|
||
// Note: On non-extended ports, there should be only one pNode.
|
||
if (((NodeAddr->atn_Network == CABLEWIDE_BROADCAST_NETWORK) ||
|
||
(pNode->an_NodeAddr.atn_Network == NodeAddr->atn_Network) ||
|
||
(!EXT_NET(pPortDesc) && (pNode->an_NodeAddr.atn_Network == UNKNOWN_NETWORK)))
|
||
|
||
&&
|
||
|
||
((NodeAddr->atn_Node == ATALK_BROADCAST_NODE) ||
|
||
(pNode->an_NodeAddr.atn_Node == NodeAddr->atn_Node)))
|
||
{
|
||
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
|
||
("AtalkNodeRefByAddr: Found: %lx.%lx for Lookup: %lx.%lx\n",
|
||
pNode->an_NodeAddr.atn_Network, pNode->an_NodeAddr.atn_Node,
|
||
NodeAddr->atn_Network, NodeAddr->atn_Node));
|
||
|
||
foundNode = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (foundNode)
|
||
{
|
||
AtalkNodeRefByPtr(pNode, pErr);
|
||
|
||
// Return a pointer to the referenced node.
|
||
if (ATALK_SUCCESS(*pErr))
|
||
{
|
||
ASSERT(ppNode != NULL);
|
||
ASSERT(pNode != NULL);
|
||
|
||
*ppNode = pNode;
|
||
}
|
||
}
|
||
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkNodeRefNextNc(
|
||
IN PATALK_NODE pNode,
|
||
IN PATALK_NODE * ppNode,
|
||
OUT PATALK_ERROR pErr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
MUST BE CALLED WITH THE PORTLOCK HELD!
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
*pErr = ATALK_FAILURE;
|
||
*ppNode = NULL;
|
||
for (; pNode != NULL; pNode = pNode->an_Next)
|
||
{
|
||
ASSERT(VALID_ATALK_NODE(pNode));
|
||
|
||
AtalkNodeRefByPtr(pNode, pErr);
|
||
if (ATALK_SUCCESS(*pErr))
|
||
{
|
||
// Ok, this node is referenced!
|
||
*ppNode = pNode;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
AtalkNodeDeref(
|
||
IN OUT PATALK_NODE pNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PPORT_DESCRIPTOR pPortDesc = pNode->an_Port;
|
||
KIRQL OldIrql;
|
||
BOOLEAN done = FALSE;
|
||
|
||
ASSERT(VALID_ATALK_NODE(pNode));
|
||
|
||
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
|
||
|
||
ASSERT(pNode->an_RefCount > 0);
|
||
if (--pNode->an_RefCount == 0)
|
||
{
|
||
done = TRUE;
|
||
}
|
||
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
|
||
|
||
if (done)
|
||
{
|
||
PATALK_NODE *ppNode;
|
||
|
||
ASSERT((pNode->an_Flags & AN_CLOSING) != 0);
|
||
|
||
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_WARN,
|
||
("AtalkNodeDeref: Freeing node %lx\n", pNode));
|
||
|
||
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
|
||
// Remove this guy from the port linkage
|
||
for (ppNode = &pNode->an_Port->pd_Nodes;
|
||
*ppNode != NULL;
|
||
ppNode = &((*ppNode)->an_Next))
|
||
{
|
||
if (*ppNode == pNode)
|
||
{
|
||
*ppNode = pNode->an_Next;
|
||
break;
|
||
}
|
||
}
|
||
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
|
||
|
||
// Dereference the port for this node
|
||
AtalkPortDereference(pPortDesc);
|
||
|
||
// Free the node structure
|
||
AtalkFreeMemory(pNode);
|
||
}
|
||
}
|
||
|
||
|