mirror of https://github.com/tongzx/nt5src
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.
783 lines
22 KiB
783 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
dirchngp.cxx
|
|
|
|
Abstract:
|
|
This module contains the internal directory change routines
|
|
|
|
Author:
|
|
Murali R. Krishnan ( MuraliK ) 16-Jan-1995
|
|
|
|
--*/
|
|
|
|
#include "TsunamiP.Hxx"
|
|
#pragma hdrstop
|
|
|
|
#include "issched.hxx"
|
|
#include "dbgutil.h"
|
|
#include <lonsi.hxx>
|
|
|
|
//
|
|
// Manifests
|
|
//
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
HANDLE g_hChangeWaitThread = NULL;
|
|
LONG g_nTsunamiThreads = 0;
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
#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;
|
|
FILE_NOTIFY_INFORMATION *pNI;
|
|
LPBYTE pMax;
|
|
|
|
//
|
|
// 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 )
|
|
{
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
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 )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Old APC notification for %s\n",
|
|
pVrm->pszDirectoryA ));
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
ASSERT( pVrm->fCachingAllowed );
|
|
|
|
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Got APC thing for %s.\n",
|
|
pVrm->pszDirectoryA ));
|
|
}
|
|
|
|
//
|
|
// Convert NotifyInfo from Unicode to Ansi
|
|
//
|
|
|
|
if ( dwBytesWritten )
|
|
{
|
|
for ( pNI = &pDci->NotifyInfo,
|
|
pMax = (LPBYTE)pNI + sizeof(pDci->NotifyInfo) + sizeof(pDci->szPathNameBuffer) ;; )
|
|
{
|
|
CHAR achAnsi[MAX_PATH+1];
|
|
|
|
pNI->FileNameLength = WideCharToMultiByte( CP_ACP,
|
|
WC_COMPOSITECHECK,
|
|
pNI->FileName,
|
|
pNI->FileNameLength / sizeof(WCHAR),
|
|
achAnsi,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL );
|
|
memcpy( pNI->FileName, achAnsi, pNI->FileNameLength );
|
|
|
|
if ( pNI->NextEntryOffset )
|
|
{
|
|
pNI = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pNI) + pNI->NextEntryOffset);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( pEntry = pDci->listCacheObjects.Flink;
|
|
pEntry != &pDci->listCacheObjects;
|
|
pEntry = pNextEntry )
|
|
{
|
|
pNextEntry = pEntry->Flink;
|
|
|
|
pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
|
|
if ( dwBytesWritten &&
|
|
( pCache->iDemux == RESERVED_DEMUX_OPEN_FILE ||
|
|
pCache->iDemux == RESERVED_DEMUX_URI_INFO ) )
|
|
{
|
|
for ( pNI = &pDci->NotifyInfo ;; )
|
|
{
|
|
if ( pCache->iDemux == RESERVED_DEMUX_URI_INFO
|
|
? ( ((PW3_URI_INFO)(pCache->pbhBlob+1))->cchName - pVrm->cchDirectoryA -1 == pNI->FileNameLength &&
|
|
!_memicmp( (LPSTR)pNI->FileName,
|
|
((PW3_URI_INFO)(pCache->pbhBlob+1))->pszName + pVrm->cchDirectoryA + 1,
|
|
pNI->FileNameLength ) )
|
|
: ( pCache->cchLength - pVrm->cchDirectoryA -1 == pNI->FileNameLength &&
|
|
!_memicmp( pCache->szPath + pVrm->cchDirectoryA + 1,
|
|
pNI->FileName,
|
|
pNI->FileNameLength ) ) )
|
|
{
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Expired entry for: %s.\n", pCache->szPath ));
|
|
}
|
|
|
|
bSuccess = DeCache( pCache, FALSE );
|
|
|
|
ASSERT( bSuccess );
|
|
|
|
break;
|
|
}
|
|
|
|
if ( pNI->NextEntryOffset )
|
|
{
|
|
pNI = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pNI) + pNI->NextEntryOffset);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if ( pCache->iDemux != RESERVED_DEMUX_PHYSICAL_OPEN_FILE )
|
|
{
|
|
IF_DEBUG( DIRECTORY_CHANGE ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Expired entry for: %s.\n", pCache->szPath ));
|
|
}
|
|
|
|
bSuccess = DeCache( pCache, FALSE );
|
|
|
|
ASSERT( bSuccess );
|
|
}
|
|
}
|
|
|
|
INC_COUNTER( pVrm->dwServiceID,
|
|
FlushesFromDirChanges );
|
|
|
|
if ( !pfnReadDirChangesW( 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->fCachingAllowed = FALSE;
|
|
|
|
CLOSE_DIRECTORY_HANDLE( pDci );
|
|
|
|
//
|
|
// 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
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PVIRTUAL_ROOT_MAPPING pVrm;
|
|
PDIRECTORY_CACHING_INFO pDci;
|
|
DWORD dwWaitResult;
|
|
HANDLE ahEvents[2];
|
|
|
|
InterlockedIncrement( &g_nTsunamiThreads );
|
|
ahEvents[0] = g_hQuit;
|
|
ahEvents[1] = g_hNewItem;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Loop through the list looking for any directories which haven't
|
|
// been added yet
|
|
//
|
|
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
for ( pEntry = GlobalVRootList.Flink;
|
|
pEntry != &GlobalVRootList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pVrm = CONTAINING_RECORD( pEntry,
|
|
VIRTUAL_ROOT_MAPPING,
|
|
GlobalListEntry );
|
|
|
|
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 ( !pfnReadDirChangesW( 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\n",
|
|
GetLastError() ));
|
|
|
|
DBG_ASSERT( pVrm->fCachingAllowed == FALSE );
|
|
|
|
if ( pDci->hDirectoryFile )
|
|
{
|
|
CLOSE_DIRECTORY_HANDLE( pDci );
|
|
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->fCachingAllowed = 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 = GlobalVRootList.Flink;
|
|
pEntry != &GlobalVRootList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pVrm = CONTAINING_RECORD(
|
|
pEntry,
|
|
VIRTUAL_ROOT_MAPPING,
|
|
GlobalListEntry );
|
|
|
|
if ( pVrm->fCachingAllowed ) {
|
|
|
|
pDci = (PDIRECTORY_CACHING_INFO) (pVrm + 1);
|
|
pVrm->fDeleted = TRUE;
|
|
CLOSE_DIRECTORY_HANDLE( pDci );
|
|
}
|
|
|
|
pEntry = pEntry->Blink;
|
|
RemoveEntryList( pEntry->Flink );
|
|
DereferenceRootMapping( pVrm );
|
|
}
|
|
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
|
|
InterlockedDecrement( &g_nTsunamiThreads );
|
|
return( 0 );
|
|
|
|
} // ChangeWaitThread
|
|
|
|
|
|
|
|
BOOL
|
|
TsDecacheVroot(
|
|
DIRECTORY_CACHING_INFO * pDci
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
This function decouples all of the items that need to be decached
|
|
from the associated tsunami data structures and derefences the
|
|
cache obj. This routine used to schedule this off to a worker thread
|
|
but this is no longer necessary as this only is called during vroot
|
|
destruction.
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
CACHE_OBJECT * pCacheTmp;
|
|
|
|
while ( !IsListEmpty( &pDci->listCacheObjects ))
|
|
{
|
|
pEntry = pDci->listCacheObjects.Flink;
|
|
|
|
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
|
|
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
|
|
|
if ( pCacheTmp->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
TsDereferenceCacheObj( pCacheTmp, FALSE );
|
|
}
|
|
|
|
return TRUE;
|
|
} // TsDecacheVroot
|
|
|
|
|
|
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 DBG
|
|
IF_DEBUG( CACHE ) {
|
|
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 ( pCacheTmp->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the object hasn't been referenced since the last TTL, throw
|
|
// it out now
|
|
//
|
|
|
|
if ( pCacheTmp->TTL == 0 ) {
|
|
|
|
//
|
|
// 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( &csVirtualRoots );
|
|
LeaveCriticalSection( &CacheTable.CriticalSection );
|
|
|
|
//
|
|
// 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, TRUE );
|
|
}
|
|
|
|
} // TsFlushTimedOutCacheObjects
|
|
|
|
|
|
#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\n",
|
|
&CacheTable,
|
|
MAX_BINS ));
|
|
|
|
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, Instance = %d iDemux = 0x%lx ref = %d, TTL = %d\n"
|
|
" hash = 0x%lx, cchLength = %d\n"
|
|
" %s\n",
|
|
pcobj,
|
|
pcobj->dwService,
|
|
pcobj->dwInstance,
|
|
pcobj->iDemux,
|
|
pcobj->references,
|
|
pcobj->TTL,
|
|
pcobj->hash,
|
|
pcobj->cchLength,
|
|
pcobj->szPath ));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //DBG
|
|
|
|
|
|
|
|
|
|
VOID
|
|
TsDumpCacheToHtml( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
DWORD cItemsOnBin = 0;
|
|
DWORD cTotalItems = 0;
|
|
DWORD i, c, cb;
|
|
|
|
EnterCriticalSection( &CacheTable.CriticalSection );
|
|
EnterCriticalSection( &csVirtualRoots );
|
|
|
|
DBG_ASSERT( lpcbBuffer != NULL && pchBuffer != NULL);
|
|
DBG_ASSERT( *lpcbBuffer > 10240);
|
|
|
|
cb = wsprintf( pchBuffer,
|
|
" CacheTable at 0x%lx, MAX_BINS=%d<br>"
|
|
"<TABLE BORDER> <TR> <TH> Bin Number </TH> "
|
|
// " <TH> # Items </TH> "
|
|
,
|
|
&CacheTable, MAX_BINS);
|
|
|
|
//
|
|
// Generate the column headings for the bins
|
|
// 0, 1, 2, ..... 9
|
|
//
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
if ( *lpcbBuffer >= cb + 20) {
|
|
cb += wsprintf( pchBuffer +cb,
|
|
"<TH>%d</TH>",
|
|
i);
|
|
}
|
|
} // for
|
|
|
|
for ( i = 0; i < MAX_BINS; i++ ) {
|
|
|
|
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
|
|
pEntry != &CacheTable.Items[i];
|
|
pEntry = pEntry->Flink, cItemsOnBin++ )
|
|
{
|
|
// count the number of items in this bin
|
|
;
|
|
}
|
|
cTotalItems += cItemsOnBin;
|
|
|
|
if ( *lpcbBuffer >= cb + 60) {
|
|
|
|
if ( i % 10 == 0) {
|
|
//
|
|
// start a new row
|
|
//
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"</TR><TR><TH>[%3d] </TH>",
|
|
i);
|
|
}
|
|
|
|
if ( cItemsOnBin > 0) {
|
|
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"<TD>%4d</TD>",
|
|
cItemsOnBin);
|
|
} else {
|
|
|
|
//
|
|
// Dump nothing for zero slots
|
|
//
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"<TD><font color=\"0x80808080\"> </font></TD>",
|
|
cItemsOnBin);
|
|
}
|
|
}
|
|
} // for all bins
|
|
|
|
|
|
if ( *lpcbBuffer >= cb + 50) {
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"</TR></TABLE><p>"
|
|
"Total Objects in bins: %d; OpenFilesInUse(%d);"
|
|
" Max Allowed=%d. <br> <hr>"
|
|
" The cached objects: "
|
|
,
|
|
cTotalItems,
|
|
CacheTable.OpenFileInUse,
|
|
CacheTable.MaxOpenFileInUse
|
|
);
|
|
}
|
|
|
|
//
|
|
// Now print the contents of each bin
|
|
//
|
|
|
|
for ( i = 0; i < MAX_BINS; i++ ) {
|
|
|
|
PCACHE_OBJECT pcobj;
|
|
|
|
if ( IsListEmpty( &CacheTable.Items[i] ))
|
|
continue;
|
|
|
|
if ( *lpcbBuffer >= cb + 60) {
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"<hr><b>============ Bin %d ==========</b><br>",
|
|
i );
|
|
}
|
|
|
|
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
|
|
pEntry != &CacheTable.Items[i];
|
|
pEntry = pEntry->Flink, cItemsOnBin++ )
|
|
{
|
|
pcobj = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
|
|
|
|
if ( *lpcbBuffer >= cb + 300) {
|
|
cb += wsprintf( pchBuffer + cb,
|
|
"[0x%lx] Svc:Inst = %d:%d; "
|
|
"iDemux=0x%lx; ref=%d; TTL=%d; "
|
|
" hash=0x%lx; "
|
|
" (%d) %s<br>",
|
|
pcobj,
|
|
pcobj->dwService,
|
|
pcobj->dwInstance,
|
|
pcobj->iDemux,
|
|
pcobj->references,
|
|
pcobj->TTL,
|
|
pcobj->hash,
|
|
pcobj->cchLength,
|
|
pcobj->szPath
|
|
);
|
|
}
|
|
} // for each item in bin
|
|
} // for each bin
|
|
|
|
LeaveCriticalSection( &csVirtualRoots );
|
|
LeaveCriticalSection( &CacheTable.CriticalSection );
|
|
|
|
*lpcbBuffer = cb;
|
|
|
|
return;
|
|
} // TsDumpCacheToHtml()
|