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.
906 lines
23 KiB
906 lines
23 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Scavengr.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Netware Redirector scavenger thread.
|
|
|
|
Author:
|
|
|
|
Manny Weiser [MannyW] 15-Feb-1993
|
|
|
|
Revision History:
|
|
|
|
Tommy Evans (tommye) 04-27-2000 MS bug 33463 - added bShuttingDown flag
|
|
to CleanupSupplementalCredentials() to force cleanup
|
|
of cached credentials when we are unloaded.
|
|
--*/
|
|
|
|
#include "Procs.h"
|
|
|
|
//
|
|
// The debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_SCAVENGER)
|
|
|
|
extern BOOLEAN WorkerRunning; // From timer.c
|
|
|
|
#ifdef NWDBG
|
|
DWORD DumpIcbFlag = 0 ;
|
|
#endif
|
|
|
|
VOID
|
|
CleanupVcbs(
|
|
LARGE_INTEGER Now
|
|
);
|
|
|
|
VOID
|
|
CleanupObjectCache(
|
|
VOID
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#ifndef QFE_BUILD
|
|
#pragma alloc_text( PAGE1, NwAllocateExtraIrpContext )
|
|
#pragma alloc_text( PAGE1, NwFreeExtraIrpContext )
|
|
#pragma alloc_text( PAGE1, CleanupScbs )
|
|
#pragma alloc_text( PAGE1, DisconnectTimedOutScbs )
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//
|
|
// Not pageable:
|
|
//
|
|
// NwScavengerRoutine - Acquires a spin lock.
|
|
// CleanupVcbs - Acquires a spin lock.
|
|
//
|
|
|
|
VOID
|
|
NwScavengerRoutine(
|
|
IN PWORK_QUEUE_ITEM WorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the scavenger. The scavenger runs
|
|
periodically in the context of an executive worker thread to
|
|
do background cleanup operations on redirector data.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - The work item for this routine.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Now;
|
|
PMDL LineChangeMdl;
|
|
PWORK_QUEUE_ITEM LineChangeWorkItem;
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
DebugTrace(+1, Dbg, "NwScavengerRoutine\n", 0);
|
|
|
|
KeQuerySystemTime( &Now );
|
|
|
|
#ifdef NWDBG
|
|
if (DumpIcbFlag != 0)
|
|
DumpIcbs();
|
|
#endif
|
|
|
|
//
|
|
// Try to free unused VCBs.
|
|
//
|
|
|
|
CleanupVcbs(Now);
|
|
|
|
//
|
|
// Try disconnect from SCBs that are timed out.
|
|
//
|
|
|
|
DisconnectTimedOutScbs(Now) ;
|
|
|
|
//
|
|
// Try to invalidate old object cache entries.
|
|
//
|
|
|
|
CleanupObjectCache();
|
|
|
|
//
|
|
// Try to free unused SCBs.
|
|
//
|
|
|
|
CleanupScbs(Now);
|
|
|
|
//
|
|
// Clean up supplemental credentials that are
|
|
// no longer being used.
|
|
//
|
|
|
|
CleanupSupplementalCredentials(Now, FALSE);
|
|
|
|
//
|
|
// Flag we're finished now to avoid deadlock in stop timer.
|
|
//
|
|
|
|
KeAcquireSpinLock( &NwScavengerSpinLock, &OldIrql );
|
|
|
|
if ( DelayedProcessLineChange ) {
|
|
|
|
DebugTrace( 0, Dbg, "Scavenger processing a delayed line change notification.\n", 0 );
|
|
|
|
LineChangeMdl = DelayedLineChangeIrp->MdlAddress;
|
|
LineChangeWorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
|
|
|
|
if ( LineChangeWorkItem == NULL ) {
|
|
|
|
//
|
|
// If we couldn't get a work queue item, just blow
|
|
// it all off for now.
|
|
//
|
|
|
|
FREE_POOL( LineChangeMdl->MappedSystemVa );
|
|
FREE_MDL( LineChangeMdl );
|
|
FREE_IRP( DelayedLineChangeIrp );
|
|
|
|
DelayedLineChangeIrp = NULL;
|
|
DelayedProcessLineChange = FALSE;
|
|
WorkerRunning = FALSE;
|
|
|
|
KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Leave WorkRunning set to TRUE so that the scavenger can't run
|
|
// while the process line change is running, but clear the line
|
|
// change flag. The FspProcessLineChange function will clear the
|
|
// WorkerRunning flag.
|
|
//
|
|
|
|
DelayedProcessLineChange = FALSE;
|
|
KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
|
|
|
|
//
|
|
// Use the user buffer field as a convenient place to remember where
|
|
// the address of the WorkQueueItem. We can get away with this since
|
|
// we don't let this IRP complete.
|
|
//
|
|
|
|
DelayedLineChangeIrp->UserBuffer = LineChangeWorkItem;
|
|
|
|
//
|
|
// Process the line change in the FSP.
|
|
//
|
|
|
|
ExInitializeWorkItem( LineChangeWorkItem, FspProcessLineChange, DelayedLineChangeIrp );
|
|
ExQueueWorkItem( LineChangeWorkItem, DelayedWorkQueue );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// No line change happened while the scavenger was running.
|
|
//
|
|
|
|
WorkerRunning = FALSE;
|
|
KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
|
|
|
|
}
|
|
|
|
//
|
|
// Unlock discardable code, if we are inactive. Don't block
|
|
// if can't get resource.
|
|
//
|
|
|
|
NwUnlockCodeSections(FALSE);
|
|
|
|
|
|
DebugTrace(-1, Dbg, "NwScavengerRoutine\n", 0);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CleanupScbs(
|
|
LARGE_INTEGER Now
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to free unused VCB structures.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PNONPAGED_SCB pNpScb;
|
|
PLIST_ENTRY NextScbQueueEntry;
|
|
PSCB pScb;
|
|
LIST_ENTRY DyingScbs;
|
|
LARGE_INTEGER KillTime ;
|
|
|
|
DebugTrace(+1, Dbg, "CleanupScbs\n", 0);
|
|
|
|
//
|
|
// Calculate KillTime = Now - 2 minutes.
|
|
//
|
|
|
|
InitializeListHead( &DyingScbs );
|
|
|
|
KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
|
|
|
|
//
|
|
// Scan through the SCBs holding the RCB.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
//
|
|
// find all SCBs that are no longer usable and put them on the dying list.
|
|
// we will take a second pass thru to remove timed out ones, based on
|
|
// what is left.
|
|
//
|
|
|
|
for (ScbQueueEntry = ScbQueue.Flink ;
|
|
ScbQueueEntry != &ScbQueue ;
|
|
ScbQueueEntry = NextScbQueueEntry )
|
|
{
|
|
|
|
pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
NextScbQueueEntry = pNpScb->ScbLinks.Flink;
|
|
|
|
if ( ( pNpScb->Reference == 0 ) &&
|
|
( ( pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart ) ||
|
|
( pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) ) )
|
|
{
|
|
DebugTrace( 0, Dbg,
|
|
"Moving SCB %08lx to dead list\n", pNpScb);
|
|
|
|
//
|
|
// The SCB has no references and is not logged in nor attached.
|
|
//
|
|
|
|
RemoveEntryList( &pNpScb->ScbLinks );
|
|
InsertHeadList( &DyingScbs, &pNpScb->ScbLinks );
|
|
}
|
|
|
|
#ifdef MSWDBG
|
|
//
|
|
// Look for blocked connections. If there's something
|
|
// queued for this server yet nothing was added or removed
|
|
// since the last time the scavenger ran then stop
|
|
//
|
|
|
|
if ((!IsListEmpty( &pNpScb->Requests ) ) &&
|
|
(pNpScb->RequestQueued == FALSE) &&
|
|
(pNpScb->RequestDequeued == FALSE )) {
|
|
|
|
DebugTrace( 0, Dbg, "Server %08lx seems to be locked up!\n", pNpScb );
|
|
ASSERT( FALSE );
|
|
|
|
} else {
|
|
|
|
pNpScb->RequestQueued = FALSE;
|
|
pNpScb->RequestDequeued = FALSE;
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Now that the dying SCBs are off the ScbQueue we can release
|
|
// the SCB spin lock.
|
|
//
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
//
|
|
// Walk the list of Dying SCBs and kill them off. Note that we are
|
|
// still holding the RCB.
|
|
//
|
|
|
|
while ( !IsListEmpty( &DyingScbs ) ) {
|
|
|
|
pNpScb = CONTAINING_RECORD( DyingScbs.Flink, NONPAGED_SCB, ScbLinks );
|
|
pScb = pNpScb->pScb;
|
|
|
|
RemoveHeadList( &DyingScbs );
|
|
NwDeleteScb( pScb );
|
|
}
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
DebugTrace(-1, Dbg, "CleanupScbs\n", 0);
|
|
|
|
}
|
|
|
|
VOID
|
|
CleanupVcbs(
|
|
LARGE_INTEGER Now
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tries to free unused VCB structures.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PLIST_ENTRY VcbQueueEntry;
|
|
PLIST_ENTRY NextVcbQueueEntry;
|
|
PNONPAGED_SCB pNpScb;
|
|
PSCB pScb;
|
|
PVCB pVcb;
|
|
LARGE_INTEGER KillTime;
|
|
|
|
NTSTATUS Status;
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
BOOLEAN VcbDeleted;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "CleanupVcbs...\n", 0 );
|
|
|
|
//
|
|
// Calculate KillTime = Now - 5 minutes.
|
|
//
|
|
|
|
KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_VCB_KEEP_TIME );
|
|
|
|
//
|
|
// Scan through the SCBs.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
ScbQueueEntry = ScbQueue.Flink;
|
|
|
|
while ( ScbQueueEntry != &ScbQueue ) {
|
|
|
|
pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
|
|
//
|
|
// Reference the SCB so that it won't go away when we release
|
|
// the SCB spin lock.
|
|
//
|
|
|
|
NwReferenceScb( pNpScb );
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
pScb = pNpScb->pScb;
|
|
|
|
if ( pScb == NULL) {
|
|
|
|
//
|
|
// This must be the permanent SCB. Just skip it.
|
|
//
|
|
|
|
ASSERT( pNpScb == &NwPermanentNpScb );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get an irp context and get to the head of the queue.
|
|
//
|
|
|
|
if ( NwAllocateExtraIrpContext( &IrpContext, pNpScb ) ) {
|
|
|
|
IrpContext->pNpScb = pNpScb;
|
|
IrpContext->pScb = pNpScb->pScb;
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
|
|
VcbDeleted = TRUE;
|
|
|
|
//
|
|
// NwCleanupVcb releases the RCB, but we can't be guaranteed
|
|
// the state of the Vcb list when we release the RCB.
|
|
//
|
|
// If we need to cleanup a VCB, release the lock, and start
|
|
// processing the list again.
|
|
//
|
|
|
|
while ( VcbDeleted ) {
|
|
|
|
VcbDeleted = FALSE;
|
|
|
|
for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink ;
|
|
VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
|
|
VcbQueueEntry = NextVcbQueueEntry ) {
|
|
|
|
pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
|
|
NextVcbQueueEntry = VcbQueueEntry->Flink;
|
|
|
|
//
|
|
// The VCB has no references, and hasn't been used for
|
|
// a long time. Kill it.
|
|
//
|
|
|
|
if ( pVcb->Reference == 0 ) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
DebugTrace(0, Dbg, "Cleaning up VCB %08lx\n", pVcb );
|
|
DebugTrace(0, Dbg, "VCB name = %wZ\n", &pVcb->Name );
|
|
|
|
// Lock down so that we can send a packet.
|
|
NwReferenceUnlockableCodeSection();
|
|
|
|
NwCleanupVcb( pVcb, IrpContext );
|
|
|
|
NwDereferenceUnlockableCodeSection ();
|
|
|
|
//
|
|
// Get back to the head of the queue, re-acquire
|
|
// the VCB, and restart the processing of this list.
|
|
//
|
|
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
VcbDeleted = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
} // for
|
|
|
|
} // while
|
|
|
|
} else {
|
|
|
|
IrpContext = NULL;
|
|
DebugTrace( 0, Dbg, "Couldn't cleanup SCB: %08lx\n", pNpScb );
|
|
|
|
}
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
}
|
|
|
|
//
|
|
// Free the irp context allocated for this SCB.
|
|
//
|
|
|
|
if ( IrpContext != NULL ) {
|
|
NwDequeueIrpContext( IrpContext, FALSE );
|
|
NwFreeExtraIrpContext( IrpContext );
|
|
IrpContext = NULL;
|
|
}
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
ScbQueueEntry = pNpScb->ScbLinks.Flink;
|
|
NwDereferenceScb( pNpScb );
|
|
}
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
DebugTrace(-1, Dbg, "CleanupVcbs -> VOID\n", 0 );
|
|
}
|
|
|
|
|
|
VOID
|
|
DisconnectTimedOutScbs(
|
|
LARGE_INTEGER Now
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine disconnects any timed out SCBs before they get
|
|
nuked by CleanupScbs() which does not disconnect.
|
|
|
|
NOTE: The SCB's are destroyed on a timeout for a couple of
|
|
reasons. The first is because if we used a reference count then
|
|
normal use of UNCs would cause us to be continually reconnecting.
|
|
Another is in FindNearestServer where its useful to collect the
|
|
Near servers that are out of connections so we can avoid them when
|
|
we iterate through the 5 nearest servers and we escalate to General
|
|
SAP response.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PNONPAGED_SCB pNpScb;
|
|
LARGE_INTEGER KillTime ;
|
|
|
|
PIRP_CONTEXT IrpContext = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "DisconnectTimedOutScbs...\n", 0 );
|
|
|
|
//
|
|
// Calculate KillTime = Now - 5 minutes.
|
|
//
|
|
|
|
KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
|
|
|
|
//
|
|
// Scan through the SCBs.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
ScbQueueEntry = ScbQueue.Flink;
|
|
|
|
while ( ScbQueueEntry != &ScbQueue )
|
|
{
|
|
|
|
pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
|
|
|
|
if ( (pNpScb != &NwPermanentNpScb) &&
|
|
(pNpScb->Reference == 0 ) &&
|
|
(pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart) )
|
|
{
|
|
//
|
|
// Reference the SCB so that it won't go away when we release
|
|
// the SCB spin lock.
|
|
//
|
|
|
|
NwReferenceScb( pNpScb );
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
//
|
|
// Not the permanent SCB and the reference count is the one
|
|
// we just added, So this is really at zero & has not been used
|
|
// for a while. Note we only allocate the IrpContext once.
|
|
//
|
|
if ( IrpContext ||
|
|
NwAllocateExtraIrpContext( &IrpContext, pNpScb ) )
|
|
{
|
|
|
|
IrpContext->pNpScb = pNpScb;
|
|
|
|
// Lock down so that we can send a packet.
|
|
NwReferenceUnlockableCodeSection();
|
|
|
|
//
|
|
// get to front of queue and recheck to make sure we are
|
|
// still with a ref count of 1.
|
|
//
|
|
NwAppendToQueueAndWait( IrpContext );
|
|
|
|
if (pNpScb->Reference == 1)
|
|
{
|
|
//
|
|
// make sure we do not reconnect.
|
|
//
|
|
ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
|
|
|
|
//
|
|
// This will result in a logoff and/or disconnect as
|
|
// need.
|
|
//
|
|
NwLogoffAndDisconnect(IrpContext, pNpScb) ;
|
|
}
|
|
|
|
NwDequeueIrpContext(IrpContext, FALSE) ;
|
|
|
|
NwDereferenceUnlockableCodeSection ();
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Could not allocate IrpContext. Oh well, we'll just leave
|
|
// this connection for the watch dog.
|
|
//
|
|
}
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
NwDereferenceScb( pNpScb );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// not timed out or is permanent SCB. dont disconnect.
|
|
//
|
|
}
|
|
|
|
ScbQueueEntry = pNpScb->ScbLinks.Flink;
|
|
}
|
|
|
|
if ( IrpContext )
|
|
NwFreeExtraIrpContext( IrpContext );
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
DebugTrace(-1, Dbg, "DisconnectTimedOutScbs -> VOID\n", 0 );
|
|
}
|
|
|
|
BOOLEAN
|
|
NwAllocateExtraIrpContext(
|
|
OUT PIRP_CONTEXT *ppIrpContext,
|
|
IN PNONPAGED_SCB pNpScb
|
|
)
|
|
{
|
|
PIRP Irp;
|
|
BOOLEAN Success = TRUE;
|
|
|
|
try {
|
|
|
|
//
|
|
// Try to allocate an IRP
|
|
//
|
|
|
|
Irp = ALLOCATE_IRP( pNpScb->Server.pDeviceObject->StackSize, FALSE );
|
|
if ( Irp == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
//
|
|
// Try to allocate an IRP Context. This will
|
|
// raise an excpetion if it fails.
|
|
//
|
|
|
|
*ppIrpContext = AllocateIrpContext( Irp );
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
} except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
|
|
Success = FALSE;
|
|
}
|
|
|
|
return( Success );
|
|
}
|
|
|
|
VOID
|
|
NwFreeExtraIrpContext(
|
|
IN PIRP_CONTEXT pIrpContext
|
|
)
|
|
{
|
|
FREE_IRP( pIrpContext->pOriginalIrp );
|
|
|
|
pIrpContext->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
|
|
|
|
FreeIrpContext( pIrpContext );
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CleanupSupplementalCredentials(
|
|
LARGE_INTEGER Now,
|
|
BOOLEAN bShuttingDown
|
|
) {
|
|
|
|
PLIST_ENTRY pLogonList;
|
|
PLIST_ENTRY pCredList;
|
|
PLOGON pLogon;
|
|
PNDS_SECURITY_CONTEXT pCredential;
|
|
LARGE_INTEGER KillTime;
|
|
|
|
DebugTrace( 0, Dbg, "CleanupSupplementalCredentials...\n", 0 );
|
|
|
|
//
|
|
// Grab the RCB to protect the logon list.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
|
|
pLogonList = LogonList.Flink;
|
|
|
|
//
|
|
// Walk the logon list.
|
|
//
|
|
|
|
while ( pLogonList != &LogonList ) {
|
|
|
|
pLogon = CONTAINING_RECORD( pLogonList, LOGON, Next );
|
|
|
|
//
|
|
// Grab the credential resource to protect the credential list.
|
|
// If we can't have exclusive access to the credential list,
|
|
// don't wait for it or we may deadlock.
|
|
//
|
|
|
|
if ( ExAcquireResourceExclusiveLite( &((pLogon)->CredentialListResource), FALSE ) ) {
|
|
|
|
pCredList = pLogon->NdsCredentialList.Flink;
|
|
|
|
while ( pCredList != &(pLogon->NdsCredentialList) ) {
|
|
BOOLEAN bRemove = FALSE;
|
|
|
|
pCredential = CONTAINING_RECORD( pCredList, NDS_SECURITY_CONTEXT, Next );
|
|
pCredList = pCredential->Next.Flink;
|
|
|
|
if (bShuttingDown) {
|
|
bRemove = TRUE;
|
|
}
|
|
else {
|
|
if ( ( IsCredentialName( &(pCredential->NdsTreeName) ) ) &&
|
|
( pCredential->SupplementalHandleCount == 0 ) ) {
|
|
|
|
//
|
|
// Calculate KillTime.
|
|
//
|
|
|
|
KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
|
|
|
|
if ( pCredential->LastUsedTime.QuadPart < KillTime.QuadPart ) {
|
|
bRemove = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** If we are supposed to clean this guy up - do it **/
|
|
|
|
if (bRemove) {
|
|
DebugTrace( 0, Dbg, "Removing credentials for %wZ\n", &pCredential->NdsTreeName );
|
|
RemoveEntryList( &pCredential->Next );
|
|
FreeNdsContext( pCredential );
|
|
}
|
|
}
|
|
|
|
ExReleaseResourceLite( &((pLogon)->CredentialListResource) );
|
|
}
|
|
|
|
pLogonList = pLogon->Next.Flink;
|
|
}
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CleanupObjectCache(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbEntry;
|
|
PLIST_ENTRY CacheEntry;
|
|
PNONPAGED_SCB NonpagedScb;
|
|
PSCB Scb;
|
|
PNDS_OBJECT_CACHE_ENTRY ObjectCache;
|
|
LARGE_INTEGER CurrentTick;
|
|
|
|
|
|
//
|
|
// Get the current tick count for checking timeouts.
|
|
//
|
|
|
|
KeQueryTickCount( &CurrentTick );
|
|
|
|
//
|
|
// Walk the SCB queue.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
ScbEntry = ScbQueue.Flink;
|
|
|
|
while( ScbEntry != &ScbQueue ) {
|
|
|
|
NonpagedScb = CONTAINING_RECORD( ScbEntry, NONPAGED_SCB, ScbLinks );
|
|
|
|
//
|
|
// Make sure this isn't the permanent SCB.
|
|
//
|
|
|
|
if( NonpagedScb != &NwPermanentNpScb ) {
|
|
|
|
//
|
|
// Reference the SCB so it won't go away when we release the SCB lock.
|
|
//
|
|
|
|
NwReferenceScb( NonpagedScb );
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
Scb = NonpagedScb->pScb;
|
|
|
|
if( Scb->ObjectCacheBuffer != NULL ) {
|
|
|
|
//
|
|
// Acquire the cache lock so that the cache can be messed with.
|
|
// This wait should never fail, but if it does, act as if there
|
|
// is no cache for this SCB.
|
|
//
|
|
|
|
Status = KeWaitForSingleObject( &(Scb->ObjectCacheLock),
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if( NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// Walk the object cache and invalidate any timed-out entries.
|
|
//
|
|
|
|
CacheEntry = Scb->ObjectCacheList.Flink;
|
|
|
|
while( CacheEntry != &(Scb->ObjectCacheList) ) {
|
|
|
|
ObjectCache = CONTAINING_RECORD( CacheEntry, NDS_OBJECT_CACHE_ENTRY, Links );
|
|
|
|
//
|
|
// If this entry has timed out, invalidate it.
|
|
//
|
|
|
|
if( ObjectCache->Scb != NULL && CurrentTick.QuadPart < ObjectCache->Timeout.QuadPart ) {
|
|
|
|
NwDereferenceScb( ObjectCache->Scb->pNpScb );
|
|
ObjectCache->Scb = NULL;
|
|
}
|
|
|
|
//
|
|
// Move to the next entry.
|
|
//
|
|
|
|
CacheEntry = CacheEntry->Flink;
|
|
}
|
|
|
|
KeReleaseSemaphore( &(Scb->ObjectCacheLock),
|
|
0,
|
|
1,
|
|
FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reacquire the SCB lock and dereference the current SCB.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
NwDereferenceScb( NonpagedScb );
|
|
}
|
|
|
|
ScbEntry = ScbEntry->Flink;
|
|
}
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
return;
|
|
}
|