mirror of https://github.com/lianthony/NT4.0
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.
730 lines
19 KiB
730 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
dirchngr.cxx
|
|
|
|
Abstract:
|
|
This module contains the internal directory change routines
|
|
|
|
Author:
|
|
Murali R. Krishnan ( MuraliK ) 16-Jan-1995
|
|
|
|
--*/
|
|
|
|
#include "TsunamiP.Hxx"
|
|
#pragma hdrstop
|
|
|
|
#include "tssched.hxx"
|
|
#include "dbgutil.h"
|
|
|
|
//
|
|
// Manifests
|
|
//
|
|
|
|
//
|
|
// This is the number of file handles that must be open before we will
|
|
// schedule a work item to do a nonblocking free of the cache items
|
|
//
|
|
|
|
#define SCHED_WORKER_THRESHOLD 200
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
HANDLE g_hChangeWaitThread = NULL;
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
VOID
|
|
DecacheWorker(
|
|
VOID * pContext
|
|
);
|
|
|
|
#if DBG
|
|
VOID
|
|
DumpCacheStructures(
|
|
VOID
|
|
);
|
|
#endif
|
|
|
|
VOID
|
|
Apc_ChangeHandler(
|
|
DWORD dwErrorCode,
|
|
DWORD dwBytesWritten,
|
|
LPOVERLAPPED lpo
|
|
)
|
|
{
|
|
PVIRTUAL_ROOT_MAPPING pVrm;
|
|
PDIRECTORY_CACHING_INFO pDci;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pNextEntry;
|
|
BOOLEAN bSuccess;
|
|
PCACHE_OBJECT pCache;
|
|
|
|
//
|
|
// The cache lock must always be taken before the root lock
|
|
//
|
|
|
|
EnterCriticalSection( &CacheTable.CriticalSection );
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
pVrm = (VIRTUAL_ROOT_MAPPING *) lpo->hEvent;
|
|
ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE );
|
|
ASSERT( pVrm->cRef > 0 );
|
|
|
|
//
|
|
// Is this item still active?
|
|
//
|
|
|
|
if ( pVrm->fDeleted )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Got APC for root that has been removed\n" ));
|
|
|
|
DereferenceRootMapping( pVrm );
|
|
goto Exit;
|
|
}
|
|
|
|
pDci = ( PDIRECTORY_CACHING_INFO )( pVrm + 1 );
|
|
|
|
//
|
|
// It's possible (though unlikely) we received a notification for the
|
|
// same item that was removed then added before we did a wait on the
|
|
// new item. This is the old notify event then.
|
|
//
|
|
|
|
if ( !pDci->fOnSystemNotifyList )
|
|
{
|
|
//IF_DEBUG( DIRECTORY_CHANGE )
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Old APC notification for %S\n",
|
|
pVrm->pszDirectory ));
|
|
goto Exit;
|
|
}
|
|
|
|
ASSERT( pVrm->bCachingAllowed );
|
|
|
|
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Got APC thing for %s.\n",
|
|
pVrm->pszDirectoryA ));
|
|
}
|
|
|
|
//
|
|
// If we have a lot of handles to close, schedule it so we don't hold
|
|
// the lock for an extended period of time, otherwise just do it
|
|
// here
|
|
//
|
|
|
|
if ( !IsListEmpty( &pDci->listCacheObjects ) &&
|
|
Configuration.Stats[ MaskIndex( pVrm->dwID ) ].CurrentOpenFileHandles
|
|
> SCHED_WORKER_THRESHOLD )
|
|
{
|
|
IF_DEBUG( DIRECTORY_CHANGE )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Apc_ChangeHandler] Scheduling non-blocking free, pDci = %x\n",
|
|
pDci ));
|
|
}
|
|
|
|
if ( !ScheduleDecache( pDci ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[Apc_ChangeHandler] Error %d scheduling decache\n",
|
|
GetLastError() ));
|
|
|
|
goto DoItNow;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
DoItNow:
|
|
for ( pEntry = pDci->listCacheObjects.Flink;
|
|
pEntry != &pDci->listCacheObjects;
|
|
pEntry = pNextEntry )
|
|
{
|
|
pNextEntry = pEntry->Flink;
|
|
|
|
pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
|
|
//
|
|
// We could selectively prune the tree but we don't
|
|
//
|
|
//
|
|
|
|
// if ( !wcsnicmp( pCache->wszPath + pVrm->cchDirectory + 1,
|
|
// pDci->NotifyInfo.FileName,
|
|
// pDci->NotifyInfo.FileNameLength ) )
|
|
{
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Expired entry for: %S.\n", pCache->wszPath ));
|
|
}
|
|
|
|
bSuccess = DeCache( pCache, FALSE );
|
|
|
|
ASSERT( bSuccess );
|
|
}
|
|
}
|
|
}
|
|
|
|
INC_COUNTER( pVrm->dwID,
|
|
FlushesFromDirChanges );
|
|
|
|
if ( !ReadDirectoryChangesW( pDci->hDirectoryFile,
|
|
(VOID *) &pDci->NotifyInfo,
|
|
sizeof( FILE_NOTIFY_INFORMATION ) +
|
|
sizeof( pDci->szPathNameBuffer ),
|
|
TRUE,
|
|
FILE_NOTIFY_VALID_MASK &
|
|
~FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
|
NULL,
|
|
&pDci->Overlapped, // hEvent used as context
|
|
Apc_ChangeHandler ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ApchChangeHandler] ReadDirectoryChanges returned %d\n",
|
|
GetLastError() ));
|
|
|
|
//
|
|
// Disable caching for this directory 'cause we aren't going to get any
|
|
// more change notifications
|
|
//
|
|
|
|
pVrm->bCachingAllowed = FALSE;
|
|
|
|
CloseHandle( pDci->hDirectoryFile );
|
|
pDci->hDirectoryFile = NULL;
|
|
|
|
//
|
|
// Decrement the ref-count as we'll never get an APC notification
|
|
//
|
|
|
|
DereferenceRootMapping( pVrm );
|
|
}
|
|
|
|
Exit:
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
LeaveCriticalSection( &CacheTable.CriticalSection );
|
|
|
|
} // Apc_ChangeHandler
|
|
|
|
DWORD
|
|
WINAPI
|
|
ChangeWaitThread(
|
|
PVOID pvParam
|
|
)
|
|
{
|
|
WAIT_THREAD_ARGS * pwta;
|
|
PLIST_ENTRY pEntry;
|
|
PVIRTUAL_ROOT_MAPPING pVrm;
|
|
PDIRECTORY_CACHING_INFO pDci;
|
|
DWORD dwWaitResult;
|
|
HANDLE ahEvents[2];
|
|
|
|
pwta = ( PWAIT_THREAD_ARGS )pvParam;
|
|
|
|
ahEvents[0] = pwta->heventStopWaiting;
|
|
ahEvents[1] = pwta->heventNewItem;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Loop through the list looking for any directories which haven't
|
|
// been added yet
|
|
//
|
|
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
for ( pEntry = pwta->plistVirtualRoots->Flink;
|
|
pEntry != pwta->plistVirtualRoots;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pVrm = CONTAINING_RECORD( pEntry, VIRTUAL_ROOT_MAPPING, list );
|
|
pDci = ( PDIRECTORY_CACHING_INFO )( pVrm + 1 );
|
|
|
|
ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE );
|
|
|
|
if ( !pDci->fOnSystemNotifyList ) {
|
|
|
|
//
|
|
// call change notify, this indicates we want change notifications
|
|
// on this set of handles. Note the wait is a one shot deal,
|
|
// once a dir has been notified, it must be readded in the
|
|
// context of this thread.
|
|
//
|
|
|
|
IF_DEBUG( DIRECTORY_CHANGE )
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Trying to wait on %S\n",
|
|
pVrm->pszDirectoryA ));
|
|
|
|
//
|
|
// Use the hEvent field of the overlapped structure as the
|
|
// context to pass to the change handler. This is allowed
|
|
// by the ReadDirectoryChanges API
|
|
//
|
|
|
|
pDci->Overlapped.hEvent = (HANDLE) pVrm;
|
|
|
|
//
|
|
// If the memory cache size is zero, don't worry about
|
|
// change notifications
|
|
//
|
|
|
|
if ( !Configuration.cbMaximumSize ||
|
|
(pVrm->dwAccessMask & (VROOT_MASK_WRITE |
|
|
VROOT_MASK_DONT_CACHE)) ||
|
|
!ReadDirectoryChangesW( pDci->hDirectoryFile,
|
|
(VOID *) &pDci->NotifyInfo,
|
|
sizeof( FILE_NOTIFY_INFORMATION ) +
|
|
sizeof( pDci->szPathNameBuffer ),
|
|
TRUE,
|
|
FILE_NOTIFY_VALID_MASK &
|
|
~FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
|
NULL,
|
|
&pDci->Overlapped, // Not used
|
|
Apc_ChangeHandler ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ChangeWaitThread] ReadDirectoryChanges"
|
|
" returned %d OR mem cache size is zero\n",
|
|
GetLastError() ));
|
|
|
|
DBG_ASSERT( pVrm->bCachingAllowed == FALSE );
|
|
|
|
if ( pDci->hDirectoryFile )
|
|
{
|
|
if ( pDci->hDirectoryFile != INVALID_HANDLE_VALUE) {
|
|
DBG_REQUIRE( CloseHandle( pDci->hDirectoryFile ));
|
|
}
|
|
pDci->hDirectoryFile = NULL;
|
|
DereferenceRootMapping( pVrm );
|
|
}
|
|
|
|
//
|
|
// We don't shrink the buffer because we use the
|
|
// fOnSystemNotifyList flag which is in portion we
|
|
// would want to shrink (and it doesn't give us a whole
|
|
// lot).
|
|
//
|
|
} else {
|
|
pVrm->bCachingAllowed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Indicate we've attempted to add the entry to the system
|
|
// notify list. Used for new items getting processed the 1st
|
|
// time
|
|
//
|
|
|
|
pDci->fOnSystemNotifyList = TRUE;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
|
|
Rewait:
|
|
dwWaitResult = WaitForMultipleObjectsEx( 2,
|
|
ahEvents,
|
|
FALSE,
|
|
INFINITE,
|
|
TRUE );
|
|
|
|
if ( dwWaitResult == WAIT_IO_COMPLETION )
|
|
{
|
|
//
|
|
// Nothing to do, the APC routine took care of everything
|
|
//
|
|
|
|
goto Rewait;
|
|
|
|
}
|
|
} while ( dwWaitResult == (WAIT_OBJECT_0 + 1) );
|
|
|
|
ASSERT( dwWaitResult == WAIT_OBJECT_0 );
|
|
|
|
//
|
|
// free the handles and all the heap.
|
|
//
|
|
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
for ( pEntry = pwta->plistVirtualRoots->Flink;
|
|
pEntry != pwta->plistVirtualRoots;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pVrm = CONTAINING_RECORD( pEntry, VIRTUAL_ROOT_MAPPING, list );
|
|
|
|
if ( pVrm->bCachingAllowed )
|
|
{
|
|
pDci = (PDIRECTORY_CACHING_INFO) (pVrm + 1);
|
|
|
|
pVrm->fDeleted = TRUE;
|
|
CloseHandle( pDci->hDirectoryFile );
|
|
}
|
|
|
|
pEntry = pEntry->Blink;
|
|
|
|
RemoveEntryList( pEntry->Flink );
|
|
|
|
DereferenceRootMapping( pVrm );
|
|
}
|
|
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
|
|
//
|
|
// We're done with our arguments, so free the memory used for them.
|
|
//
|
|
|
|
FREE( pwta );
|
|
|
|
return( 0 );
|
|
|
|
} // ChangeWaitThread
|
|
|
|
BOOL
|
|
ScheduleDecache(
|
|
DIRECTORY_CACHING_INFO * pDci
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function decouples all of the items that need to be decached
|
|
from the associated tsunami data structures so a worker thread can
|
|
be scheduled to do the actual handle closes etc.
|
|
|
|
The reason this is necessary is because the APC handler has to keep
|
|
the virtual root lock while doing the free and re-doing the change
|
|
dir notification. This prevents requests from being serviced while
|
|
the lock is taken.
|
|
|
|
This routine assumes the cache table lock and virtual root lock are
|
|
taken
|
|
|
|
Arguments:
|
|
|
|
pDci - Directory change blob that needs to have its contents decached
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if any failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwCookie;
|
|
LIST_ENTRY * pEntry;
|
|
LIST_ENTRY * pNextEntry;
|
|
LIST_ENTRY * pFlink;
|
|
LIST_ENTRY * pBlink;
|
|
CACHE_OBJECT * pCacheTmp;
|
|
LIST_ENTRY * pListHead = NULL;
|
|
|
|
|
|
if ( IsListEmpty( &pDci->listCacheObjects ))
|
|
return TRUE;
|
|
|
|
//
|
|
// Save the flink & blink in case we can't schedule a work item so we can
|
|
// restore the list and do it inline
|
|
//
|
|
|
|
pFlink = pDci->listCacheObjects.Flink;
|
|
pBlink = pDci->listCacheObjects.Blink;
|
|
|
|
//
|
|
// This becomes the context for the work routine
|
|
//
|
|
|
|
pListHead = (LIST_ENTRY *) ALLOC( sizeof(LIST_ENTRY) );
|
|
|
|
if ( !pListHead )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeListHead( pListHead );
|
|
|
|
//
|
|
// We decache all of the items on this cache list and put them onto
|
|
// our listhead context
|
|
//
|
|
|
|
for ( pEntry = pDci->listCacheObjects.Flink;
|
|
pEntry != &pDci->listCacheObjects;
|
|
pEntry = pNextEntry
|
|
)
|
|
{
|
|
pNextEntry = pEntry->Flink;
|
|
|
|
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
|
|
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
|
|
|
//
|
|
// Removes this cache item from all of the lists. It had better be
|
|
// on the cache lists otherwise somebody is mucking with the list
|
|
// without taking the lock.
|
|
//
|
|
|
|
if ( !RemoveCacheObjFromLists( pCacheTmp, FALSE ) )
|
|
{
|
|
ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
|
|
InsertTailList( pListHead, &pCacheTmp->DirChangeList );
|
|
}
|
|
|
|
dwCookie = ScheduleWorkItem( (PFN_SCHED_CALLBACK) DecacheWorker,
|
|
pListHead,
|
|
0 );
|
|
|
|
//
|
|
// If we failed, restore the list pointer and return FALSE, we'll just
|
|
// have to do the free while holding the locks
|
|
//
|
|
|
|
if ( !dwCookie )
|
|
{
|
|
|
|
pDci->listCacheObjects.Flink = pFlink;
|
|
pDci->listCacheObjects.Blink = pBlink;
|
|
pBlink->Flink = &pDci->listCacheObjects;
|
|
pFlink->Blink = &pDci->listCacheObjects;
|
|
|
|
FREE( pListHead );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
DecacheWorker(
|
|
VOID * pContext
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function is called by the scheduling thread to decache the list
|
|
of decouple cache items w/o having to take the cache locks.
|
|
|
|
Arguments:
|
|
|
|
pContext - Pointer to a list entry
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pListHead = (LIST_ENTRY *) pContext;
|
|
CACHE_OBJECT * pCacheObject;
|
|
LIST_ENTRY * pEntry;
|
|
|
|
IF_DEBUG( CACHE )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[DecacheWorker] Decaching items\n" ));
|
|
}
|
|
|
|
while ( !IsListEmpty( pListHead ))
|
|
{
|
|
pEntry = RemoveHeadList( pListHead );
|
|
|
|
pCacheObject = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
|
|
#if DBG
|
|
pCacheObject->DirChangeList.Flink = pCacheObject->DirChangeList.Blink = NULL;
|
|
#endif
|
|
ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
|
|
|
|
//
|
|
// Undo the ref count we did when we took the items off the binlist.
|
|
// This will almost always cause this item to be deleted.
|
|
//
|
|
|
|
TsDereferenceCacheObj( pCacheObject );
|
|
}
|
|
|
|
FREE( pListHead );
|
|
}
|
|
|
|
VOID
|
|
TsFlushTimedOutCacheObjects(
|
|
VOID
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function walks all cache objects and decrements the TTL of the object.
|
|
When the TTL reaches zero, the object is removed from the cache.
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
LIST_ENTRY * pNextEntry;
|
|
CACHE_OBJECT * pCacheTmp;
|
|
LIST_ENTRY ListHead;
|
|
|
|
InitializeListHead( &ListHead );
|
|
|
|
EnterCriticalSection( &CacheTable.CriticalSection );
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
IF_DEBUG( CACHE ) {
|
|
#if DBG
|
|
DumpCacheStructures();
|
|
#endif
|
|
}
|
|
|
|
for ( pEntry = CacheTable.MruList.Flink;
|
|
pEntry != &CacheTable.MruList;
|
|
pEntry = pNextEntry
|
|
)
|
|
{
|
|
pNextEntry = pEntry->Flink;
|
|
|
|
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
|
|
|
|
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
|
|
|
//
|
|
// If the object hasn't been referenced since the last TTL, throw
|
|
// it out now
|
|
//
|
|
|
|
if ( !pCacheTmp->TTL )
|
|
{
|
|
//
|
|
// Removes this cache item from all of the lists. It had better be
|
|
// on the cache lists otherwise somebody is mucking with the list
|
|
// without taking the lock. We put it on a temporary list that
|
|
// we'll traverse after we release the locks
|
|
//
|
|
|
|
if ( !RemoveCacheObjFromLists( pCacheTmp, FALSE ) )
|
|
{
|
|
ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
|
|
InsertTailList( &ListHead, &pCacheTmp->DirChangeList );
|
|
}
|
|
else
|
|
{
|
|
pCacheTmp->TTL--;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &CacheTable.CriticalSection );
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
|
|
//
|
|
// Now do the dereferences which may actually close the objects now that
|
|
// we don't have to hold the locks
|
|
//
|
|
|
|
for ( pEntry = ListHead.Flink;
|
|
pEntry != &ListHead;
|
|
pEntry = pNextEntry )
|
|
{
|
|
pNextEntry = pEntry->Flink;
|
|
|
|
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
|
|
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
|
|
|
TsDereferenceCacheObj( pCacheTmp );
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
DumpCacheStructures(
|
|
VOID
|
|
)
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
DWORD cItemsOnBin = 0;
|
|
DWORD cTotalItems = 0;
|
|
DWORD i, c;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[DumpCacheStructures] CacheTable at 0x%lx, MAX_BINS = %d, MemoryInUse = %d\n",
|
|
&CacheTable,
|
|
MAX_BINS,
|
|
CacheTable.MemoryInUse ));
|
|
|
|
for ( i = 0; i < MAX_BINS; i++ )
|
|
{
|
|
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
|
|
pEntry != &CacheTable.Items[i];
|
|
pEntry = pEntry->Flink, cItemsOnBin++, cTotalItems++ )
|
|
{
|
|
;
|
|
}
|
|
|
|
if ( cItemsOnBin > 0) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Bin[%3d] %4d\n",
|
|
i,
|
|
cItemsOnBin ));
|
|
}
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Total Objects in bins: %d\n",
|
|
cTotalItems ));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"=====================================================\n" ));
|
|
|
|
//
|
|
// Now print the contents of each bin
|
|
//
|
|
|
|
for ( i = 0; i < MAX_BINS; i++ )
|
|
{
|
|
PCACHE_OBJECT pcobj;
|
|
|
|
if ( IsListEmpty( &CacheTable.Items[i] ))
|
|
continue;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"================== Bin %d ==================\n",
|
|
i ));
|
|
|
|
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
|
|
pEntry != &CacheTable.Items[i];
|
|
pEntry = pEntry->Flink, cItemsOnBin++ )
|
|
{
|
|
pcobj = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"CACHE_OBJECT[0x%lx] Service = %d, iDemux = 0x%lx ref = %d, TTL = %d\n"
|
|
" hash = 0x%lx, cchLength = %d, User Size = %d\n"
|
|
" %S\n",
|
|
pcobj,
|
|
pcobj->dwService,
|
|
pcobj->iDemux,
|
|
pcobj->references,
|
|
pcobj->TTL,
|
|
pcobj->hash,
|
|
pcobj->cchLength,
|
|
pcobj->UserValue,
|
|
pcobj->wszPath ));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //DBG
|
|
|
|
|