1595 lines
36 KiB
C++
1595 lines
36 KiB
C++
/**********************************************************************/
|
||
/** Microsoft Windows NT **/
|
||
/** Copyright(c) Microsoft Corp., 1998 **/
|
||
/**********************************************************************/
|
||
|
||
/*
|
||
tsunami.cxx
|
||
|
||
This module contains most of the public Tsunami Cache routines.
|
||
|
||
FILE HISTORY:
|
||
MCourage 09-Dec-1997 Created
|
||
*/
|
||
|
||
#include <tsunami.hxx>
|
||
#include <inetinfo.h>
|
||
#include <issched.hxx>
|
||
#include "inetreg.h"
|
||
#include "globals.hxx"
|
||
#include "tsunamip.hxx"
|
||
#include <inetsvcs.h>
|
||
#include "metacach.hxx"
|
||
#include "filecach.hxx"
|
||
#include "blobcach.hxx"
|
||
#include "atq.h"
|
||
#include "tracelog.h"
|
||
#include <lkrhash.h>
|
||
#include "filehash.hxx"
|
||
#include "blobhash.hxx"
|
||
#include "tlcach.h"
|
||
#include "etagmb.h"
|
||
|
||
BOOL g_fCacheSecDesc = TRUE;
|
||
|
||
//
|
||
// from TsInit.cxx
|
||
//
|
||
|
||
HANDLE g_hQuit = NULL;
|
||
HANDLE g_hNewItem = NULL;
|
||
BOOL g_fW3OnlyNoAuth = FALSE;
|
||
BOOL TsNoDirOpenSupport = FALSE;
|
||
|
||
|
||
#if TSUNAMI_REF_DEBUG
|
||
PTRACE_LOG RefTraceLog;
|
||
#endif // TSUNAMI_REF_DEBUG
|
||
|
||
//
|
||
// The TTL to scavenge the cache and the id of the scheduled work item of the
|
||
// next scheduled scavenge
|
||
//
|
||
|
||
DWORD g_cmsecObjectCacheTTL = (INETA_DEF_OBJECT_CACHE_TTL * 1000);
|
||
DWORD g_dwObjectCacheCookie = 0;
|
||
|
||
# define MIN_CACHE_SCAVENGE_TIME (5*1000) // 5 seconds
|
||
|
||
|
||
//
|
||
// Disables Tsunami Caching
|
||
//
|
||
|
||
BOOL DisableTsunamiCaching = FALSE;
|
||
|
||
//
|
||
// DisableSPUD
|
||
//
|
||
|
||
BOOL DisableSPUD = FALSE;
|
||
|
||
//
|
||
// Allows us to mask the invalid flags
|
||
//
|
||
|
||
DWORD TsValidCreateFileOptions = TS_IIS_VALID_FLAGS;
|
||
|
||
//
|
||
// from globals.cxx
|
||
//
|
||
CONFIGURATION Configuration;
|
||
BOOL g_fDisableCaching = FALSE;
|
||
|
||
|
||
//
|
||
// Initialization and cleanup
|
||
//
|
||
|
||
|
||
BOOL
|
||
Tsunami_Initialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Sets up all the core caches. Call this before using any
|
||
cache routines.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
TRUE on success
|
||
--*/
|
||
{
|
||
HRESULT hr;
|
||
HKEY hKey;
|
||
DWORD dwType;
|
||
DWORD nBytes;
|
||
DWORD dwValue;
|
||
DWORD dwMaxFile;
|
||
DWORD err;
|
||
|
||
#if TSUNAMI_REF_DEBUG
|
||
RefTraceLog = CreateRefTraceLog(
|
||
256, // LogSize
|
||
0 // ExtraBytesInHeader
|
||
);
|
||
#endif // TSUNAMI_REF_DEBUG
|
||
|
||
//
|
||
// Initialize global events
|
||
//
|
||
|
||
g_hQuit = IIS_CREATE_EVENT(
|
||
"g_hQuit",
|
||
&g_hQuit,
|
||
TRUE,
|
||
FALSE
|
||
);
|
||
|
||
g_hNewItem = IIS_CREATE_EVENT(
|
||
"g_hNewItem",
|
||
&g_hNewItem,
|
||
FALSE,
|
||
FALSE
|
||
);
|
||
|
||
if ( (g_hQuit == NULL) || (g_hNewItem == NULL) ) {
|
||
goto Failure;
|
||
}
|
||
|
||
//
|
||
// Set defaults
|
||
//
|
||
|
||
MEMORYSTATUS ms;
|
||
ms.dwLength = sizeof(MEMORYSTATUS);
|
||
GlobalMemoryStatus( &ms );
|
||
|
||
//
|
||
// default is 1K files per 32MB of physical memory after the 1st 8MB,
|
||
// minimum INETA_MIN_DEF_FILE_HANDLE
|
||
//
|
||
|
||
if ( ms.dwTotalPhys > 8 * 1024 * 1024 )
|
||
{
|
||
dwMaxFile = (DWORD)(ms.dwTotalPhys - 8 * 1024 * 1024) / ( 32 * 1024 );
|
||
if ( dwMaxFile < INETA_MIN_DEF_FILE_HANDLE )
|
||
{
|
||
dwMaxFile = INETA_MIN_DEF_FILE_HANDLE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dwMaxFile = INETA_MIN_DEF_FILE_HANDLE;
|
||
}
|
||
|
||
//
|
||
// If this is not a NTS, disable tsunami caching by default
|
||
//
|
||
|
||
DisableSPUD = !AtqSpudInitialized();
|
||
|
||
|
||
if ( !TsIsNtServer() ) {
|
||
DisableTsunamiCaching = TRUE;
|
||
DisableSPUD = TRUE;
|
||
}
|
||
|
||
DisableSPUD = FALSE;
|
||
|
||
//
|
||
// no overlapped i/o in win95.
|
||
//
|
||
|
||
if ( TsIsWindows95() ) {
|
||
TsCreateFileFlags = FILE_FLAG_SEQUENTIAL_SCAN;
|
||
TsCreateFileShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||
TsNoDirOpenSupport = TRUE;
|
||
// |FILE_FLAG_BACKUP_SEMANTICS;
|
||
}
|
||
|
||
//
|
||
// Read the registry key to see whether tsunami caching is enabled
|
||
//
|
||
|
||
err = RegOpenKeyEx(
|
||
HKEY_LOCAL_MACHINE,
|
||
INETA_PARAMETERS_KEY,
|
||
0,
|
||
KEY_READ,
|
||
&hKey
|
||
);
|
||
|
||
if ( err == ERROR_SUCCESS ) {
|
||
|
||
//
|
||
// This cannot be overridded in win95
|
||
//
|
||
|
||
if ( !TsIsWindows95() ) {
|
||
nBytes = sizeof(dwValue);
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_DISABLE_TSUNAMI_CACHING,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
DisableTsunamiCaching = (BOOL)dwValue;
|
||
}
|
||
|
||
nBytes = sizeof(dwValue);
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_DISABLE_TSUNAMI_SPUD,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
DisableSPUD = (BOOL)dwValue;
|
||
if ( DisableSPUD ) {
|
||
DbgPrint("DisableCacheOplocks set to TRUE in Registry.\n");
|
||
} else {
|
||
DbgPrint("DisableCacheOplocks set to FALSE in Registry.\n");
|
||
}
|
||
DbgPrint("The Registry Setting will override the default.\n");
|
||
}
|
||
|
||
//
|
||
// How big do files have to be before we stop caching them
|
||
//
|
||
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_MAX_CACHED_FILE_SIZE,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
|
||
g_liFileCacheByteThreshold.LowPart = dwValue;
|
||
} else {
|
||
|
||
g_liFileCacheByteThreshold.LowPart = INETA_DEF_MAX_CACHED_FILE_SIZE;
|
||
}
|
||
|
||
g_liFileCacheByteThreshold.HighPart = 0; // Sorry Mr. > 4gb file.
|
||
|
||
|
||
//
|
||
// How big is the memory cache in megabytes
|
||
//
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_MEM_CACHE_SIZE,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
|
||
//
|
||
// set the size in megabytes
|
||
//
|
||
g_dwMemCacheSize = dwValue * (1024 * 1024);
|
||
} else {
|
||
|
||
g_dwMemCacheSize = INETA_DEF_MEM_CACHE_SIZE;
|
||
}
|
||
|
||
//
|
||
// Do we use the sequential read flag to read files?
|
||
//
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_ENABLE_SEQUENTIAL_READ,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
|
||
g_bEnableSequentialRead = dwValue ? 1 : 0;
|
||
} else {
|
||
|
||
g_bEnableSequentialRead = INETA_DEF_ENABLE_SEQUENTIAL_READ;
|
||
}
|
||
|
||
}
|
||
|
||
if ( g_fW3OnlyNoAuth )
|
||
{
|
||
//
|
||
// TODO: investigate is security descriptor caching
|
||
// can be used in the non-SYSTEM account case.
|
||
//
|
||
|
||
g_fCacheSecDesc = FALSE;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// read the enable cache sec desc flag
|
||
//
|
||
|
||
nBytes = sizeof(dwValue);
|
||
err = RegQueryValueEx(
|
||
hKey,
|
||
INETA_CACHE_USE_ACCESS_CHECK,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwValue,
|
||
&nBytes
|
||
);
|
||
|
||
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
||
g_fCacheSecDesc = !!dwValue;
|
||
}
|
||
else {
|
||
g_fCacheSecDesc = INETA_DEF_CACHE_USE_ACCESS_CHECK;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Read the maximum # of files in cache
|
||
//
|
||
|
||
nBytes = sizeof(dwValue);
|
||
if ( RegQueryValueEx(
|
||
hKey,
|
||
INETA_MAX_OPEN_FILE,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE) &dwValue,
|
||
&nBytes
|
||
) == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
{
|
||
dwMaxFile = dwValue;
|
||
}
|
||
|
||
RegCloseKey( hKey );
|
||
|
||
}
|
||
|
||
//
|
||
// if tsunami caching is disabled, set the flags accordingly
|
||
//
|
||
|
||
if ( DisableTsunamiCaching ) {
|
||
g_fDisableCaching = TRUE;
|
||
TsValidCreateFileOptions = TS_PWS_VALID_FLAGS;
|
||
g_fCacheSecDesc = FALSE;
|
||
}
|
||
|
||
//
|
||
// Initialize the ETag Metabase Change Number
|
||
//
|
||
|
||
hr = ETagChangeNumber::Create();
|
||
if ( FAILED(hr) ) {
|
||
goto Failure;
|
||
}
|
||
|
||
//
|
||
// Initialize the directory change manager
|
||
//
|
||
if ( !DcmInitialize( ) ) {
|
||
goto Failure;
|
||
}
|
||
|
||
//
|
||
// Initialize the tsunami cache manager
|
||
//
|
||
|
||
if ( !FileCache_Initialize( dwMaxFile )) {
|
||
goto Failure;
|
||
}
|
||
|
||
|
||
if ( !MetaCache_Initialize() ) {
|
||
goto Failure;
|
||
}
|
||
|
||
if ( !BlobCache_Initialize() ) {
|
||
goto Failure;
|
||
}
|
||
|
||
return( TRUE );
|
||
|
||
Failure:
|
||
|
||
IIS_PRINTF( ( buff, "Tsunami_Initialize() Failed. Error = %d\n",
|
||
GetLastError()));
|
||
|
||
if ( g_hQuit )
|
||
{
|
||
CloseHandle( g_hQuit );
|
||
g_hQuit = NULL;
|
||
}
|
||
|
||
if ( g_hNewItem )
|
||
{
|
||
CloseHandle( g_hNewItem );
|
||
g_hNewItem = NULL;
|
||
}
|
||
|
||
return FALSE;
|
||
} // Tsunami_Initialize
|
||
|
||
VOID
|
||
Tsunami_Terminate(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Cleans up all the core caches.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
DWORD dwResult;
|
||
|
||
if ( !SetEvent( g_hQuit ) ) {
|
||
IIS_PRINTF((buff,
|
||
"No Quit event posted for Tsunami. No Cleanup\n"));
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Flush all items from the cache
|
||
//
|
||
|
||
TsCacheFlush( 0 );
|
||
|
||
//
|
||
// Synchronize with our thread so we don't leave here before the
|
||
// thread has finished cleaning up
|
||
//
|
||
|
||
|
||
CloseHandle( g_hQuit );
|
||
CloseHandle( g_hNewItem );
|
||
|
||
|
||
BlobCache_Terminate();
|
||
MetaCache_Terminate();
|
||
FileCache_Terminate();
|
||
DcmTerminate();
|
||
|
||
ETagChangeNumber::Destroy();
|
||
|
||
#if TSUNAMI_REF_DEBUG
|
||
if( RefTraceLog != NULL ) {
|
||
DestroyRefTraceLog( RefTraceLog );
|
||
RefTraceLog = NULL;
|
||
}
|
||
#endif // TSUNAMI_REF_DEBUG
|
||
|
||
} // Tsunami_Terminate
|
||
|
||
|
||
//
|
||
// Scavenger routines
|
||
//
|
||
|
||
|
||
BOOL
|
||
FileFlushFilterTTL(
|
||
TS_OPEN_FILE_INFO * pFileInfo,
|
||
PVOID pv
|
||
)
|
||
{
|
||
if (pFileInfo->GetIORefCount()) {
|
||
//
|
||
// Try not to time out entries which are in use for I/O.
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
if (pFileInfo->GetTTL() == 0) {
|
||
pFileInfo->TraceCheckpointEx(TS_MAGIC_TIMEOUT, 0, 0);
|
||
return TRUE;
|
||
} else {
|
||
if (pFileInfo->IsInitialized()) {
|
||
pFileInfo->DecrementTTL();
|
||
}
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
BlobFlushFilterTTL(
|
||
PBLOB_HEADER pBlob,
|
||
PVOID pv
|
||
)
|
||
{
|
||
if (pBlob->TTL == 0) {
|
||
pBlob->TraceCheckpointEx(TS_MAGIC_TIMEOUT, 0, 0);
|
||
return TRUE;
|
||
} else {
|
||
pBlob->TTL--;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
WINAPI
|
||
CacheScavenger(
|
||
VOID * pContext
|
||
)
|
||
{
|
||
FilteredFlushFileCache(FileFlushFilterTTL, NULL);
|
||
FilteredFlushBlobCache(BlobFlushFilterTTL, NULL);
|
||
}
|
||
|
||
|
||
BOOL
|
||
InitializeCacheScavenger(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This function kicks off the scheduled tsunami object cache scavenger
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
TRUE on success
|
||
--*/
|
||
{
|
||
HKEY hkey;
|
||
|
||
//
|
||
// Schedule a scavenger to close all of the objects that haven't been
|
||
// referenced in the last ttl
|
||
//
|
||
|
||
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
||
INETA_PARAMETERS_KEY,
|
||
0,
|
||
KEY_READ,
|
||
&hkey ))
|
||
{
|
||
DWORD dwType;
|
||
DWORD nBytes;
|
||
DWORD dwValue;
|
||
|
||
nBytes = sizeof(dwValue);
|
||
if ( RegQueryValueEx(
|
||
hkey,
|
||
INETA_OBJECT_CACHE_TTL,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE) &dwValue,
|
||
&nBytes
|
||
) == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
{
|
||
g_cmsecObjectCacheTTL = dwValue;
|
||
} else {
|
||
g_cmsecObjectCacheTTL = 0;
|
||
}
|
||
|
||
|
||
//
|
||
// Don't schedule anything if the scavenger should be disabled
|
||
//
|
||
|
||
if ( g_cmsecObjectCacheTTL == 0xffffffff )
|
||
{
|
||
RegCloseKey( hkey );
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// The registry setting is in seconds, convert to milliseconds
|
||
//
|
||
|
||
g_cmsecObjectCacheTTL *= 1000;
|
||
|
||
//
|
||
// Supply the default if no value was specified
|
||
//
|
||
|
||
if ( !g_cmsecObjectCacheTTL )
|
||
{
|
||
g_cmsecObjectCacheTTL = INETA_DEF_OBJECT_CACHE_TTL * 1000;
|
||
}
|
||
|
||
RegCloseKey( hkey );
|
||
}
|
||
|
||
//
|
||
// Require a minimum of thirty seconds
|
||
//
|
||
|
||
g_cmsecObjectCacheTTL = max( g_cmsecObjectCacheTTL,
|
||
MIN_CACHE_SCAVENGE_TIME );
|
||
|
||
g_dwObjectCacheCookie = ScheduleWorkItem(
|
||
CacheScavenger,
|
||
NULL,
|
||
g_cmsecObjectCacheTTL,
|
||
TRUE ); // Periodic
|
||
|
||
if ( !g_dwObjectCacheCookie )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
TerminateCacheScavenger(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Stops the cache scavenger
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
if ( g_dwObjectCacheCookie )
|
||
{
|
||
RemoveWorkItem( g_dwObjectCacheCookie );
|
||
g_dwObjectCacheCookie = 0;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Blob memory management
|
||
//
|
||
|
||
|
||
BOOL
|
||
TsAllocate(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN ULONG cbSize,
|
||
IN OUT PVOID * ppvNewBlock
|
||
)
|
||
{
|
||
return( TsAllocateEx( TSvcCache,
|
||
cbSize,
|
||
ppvNewBlock,
|
||
NULL ) );
|
||
}
|
||
|
||
BOOL
|
||
TsAllocateEx(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN ULONG cbSize,
|
||
IN OUT PVOID * ppvNewBlock,
|
||
OPTIONAL PUSER_FREE_ROUTINE pfnFreeRoutine
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates a memory block for the calling server.
|
||
|
||
The returned block is suitable for use as a parameter to
|
||
TsCacheDirectoryBlob(). Blocks allocated by this function
|
||
must either be cached or freed with TsFree(). Freeing of
|
||
cached blocks will be handled by the cache manager.
|
||
|
||
Anything allocated with this routine MUST be derived from
|
||
BLOB_HEADER!
|
||
|
||
Arguments:
|
||
|
||
TSvcCache - An initialized TSVC_CACHE structure.
|
||
|
||
cbSize - Number of bytes to allocate. (Must be strictly
|
||
greater than zero.)
|
||
|
||
ppvNewBlock - Address of a pointer to store the new block's
|
||
address in.
|
||
|
||
pfnFreeRoutine - pointer to a routine that will be called to
|
||
clean up the block when it is decached.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The allocation succeeded, and *ppvNewBlock points to
|
||
at least cbSize accessable bytes.
|
||
|
||
FALSE - The allocation failed.
|
||
|
||
--*/
|
||
{
|
||
CBlobKey * pBlobKey;
|
||
PBLOB_HEADER pbhNewBlock;
|
||
|
||
DBG_ASSERT( cbSize > 0 );
|
||
DBG_ASSERT( ppvNewBlock != NULL );
|
||
|
||
//
|
||
// allocate the blob and the key while we're at it.
|
||
//
|
||
pBlobKey = (CBlobKey *) ALLOC(cbSize + sizeof(CBlobKey));
|
||
|
||
if ( pBlobKey != NULL )
|
||
{
|
||
//
|
||
// If the allocation succeeded, we return a pointer to
|
||
// the new structure which is directly preceded by it's key.
|
||
//
|
||
|
||
pbhNewBlock = (PBLOB_HEADER) (pBlobKey + 1);
|
||
*ppvNewBlock = ( PVOID )( pbhNewBlock );
|
||
|
||
//
|
||
// Set up the BLOB_HEADER: Normal flags and stored allocation
|
||
// size.
|
||
//
|
||
|
||
pbhNewBlock->Signature = TS_BLOB_SIGNATURE;
|
||
pbhNewBlock->pBlobKey = pBlobKey;
|
||
|
||
pbhNewBlock->IsCached = FALSE;
|
||
pbhNewBlock->pfnFreeRoutine = pfnFreeRoutine;
|
||
pbhNewBlock->lRefCount = 0;
|
||
pbhNewBlock->TTL = 1;
|
||
pbhNewBlock->pSecDesc = NULL;
|
||
pbhNewBlock->hLastSuccessAccessToken = INVALID_HANDLE_VALUE;
|
||
|
||
pBlobKey->m_pszPathName = NULL;
|
||
pBlobKey->m_cbPathName = 0;
|
||
pBlobKey->m_dwService = TSvcCache.GetServiceId();
|
||
pBlobKey->m_dwInstance = TSvcCache.GetInstanceId();
|
||
pBlobKey->m_dwDemux = 0;
|
||
|
||
pbhNewBlock->TraceCheckpointEx(TS_MAGIC_ALLOCATE, (PVOID) (ULONG_PTR) cbSize, pfnFreeRoutine);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// The allocation failed, and we need to return NULL
|
||
//
|
||
|
||
*ppvNewBlock = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
BOOL
|
||
TsFree(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PVOID pvOldBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function frees a memory block allocated with TsAllocate().
|
||
|
||
Blocks that are currently cached cannot be freed with this
|
||
function.
|
||
|
||
Arguments:
|
||
|
||
TSvcCache - An initialized TSVC_CACHE structure.
|
||
|
||
|
||
pvOldBlock - The address of the block to free. (Must be
|
||
non-NULL.)
|
||
|
||
Return Value:
|
||
|
||
TRUE - The block was freed. The pointer pvOldBlock is no longer
|
||
valid.
|
||
|
||
FALSE - The block was not freed. Possible reasons include:
|
||
|
||
- pvOldBlock does not point to a block allocated with
|
||
TsAllocate().
|
||
|
||
- pvOldBlock points to a block that has been cached
|
||
with CacheDirectoryBlob().
|
||
|
||
- pServiceInfo does not point to a valid SERVICE_INFO
|
||
structure.
|
||
|
||
--*/
|
||
{
|
||
BOOL bSuccess;
|
||
PBLOB_HEADER pbhOldBlock;
|
||
CBlobKey * pRealOldBlock;
|
||
|
||
DBG_ASSERT( pvOldBlock != NULL );
|
||
|
||
//
|
||
// Adjust the input pointer to refer to the BLOB_HEADER.
|
||
//
|
||
|
||
pbhOldBlock = (( PBLOB_HEADER )pvOldBlock );
|
||
|
||
DBG_ASSERT( TS_BLOB_SIGNATURE == pbhOldBlock->Signature );
|
||
|
||
//
|
||
// Track memory corruption in free builds.
|
||
//
|
||
|
||
if ( TS_BLOB_SIGNATURE != pbhOldBlock->Signature ) {
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If the Blob is currently in the cache, we can't free it.
|
||
// Check for this in the Blob's flags, and fail if it
|
||
// occurs.
|
||
//
|
||
|
||
if ( pbhOldBlock->IsCached )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"A service (%d) has attempted to TsFree a BLOB that it put in the cache.",
|
||
TSvcCache.GetServiceId() ));
|
||
BREAKPOINT();
|
||
|
||
bSuccess = FALSE;
|
||
}
|
||
else
|
||
{
|
||
pbhOldBlock->Signature = TS_FREE_BLOB_SIGNATURE;
|
||
|
||
if ( pbhOldBlock->pfnFreeRoutine )
|
||
{
|
||
bSuccess = pbhOldBlock->pfnFreeRoutine( pvOldBlock );
|
||
}
|
||
else
|
||
{
|
||
bSuccess = TRUE;
|
||
}
|
||
|
||
if ( bSuccess )
|
||
{
|
||
//
|
||
// Free the memory used by the Blob.
|
||
//
|
||
pRealOldBlock = ((CBlobKey *) pvOldBlock) - 1;
|
||
|
||
DBG_ASSERT( NULL == pRealOldBlock->m_pszPathName );
|
||
|
||
pbhOldBlock->TraceCheckpointEx(TS_MAGIC_DELETE_NC,
|
||
(PVOID) (ULONG_PTR) (pRealOldBlock->m_dwDemux),
|
||
pbhOldBlock->pfnFreeRoutine);
|
||
|
||
bSuccess = !!FREE( pRealOldBlock );
|
||
|
||
|
||
/*
|
||
DEC_COUNTER( TSvcCache.GetServiceId(),
|
||
CurrentObjects );
|
||
*/
|
||
}
|
||
|
||
}
|
||
|
||
return( bSuccess );
|
||
} // TsFree
|
||
|
||
|
||
//
|
||
// Standard cache operations
|
||
//
|
||
|
||
BOOL
|
||
TsCacheDirectoryBlob(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PCSTR pszDirectoryName,
|
||
IN ULONG cchDirectoryName,
|
||
IN ULONG iDemultiplexor,
|
||
IN PVOID pvBlob,
|
||
IN BOOLEAN bKeepCheckedOut,
|
||
IN PSECURITY_DESCRIPTOR pSecDesc
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function associates the Blob given as input with the specified
|
||
directory and demultiplexing number. Services should use this
|
||
function to add a Blob to the cache.
|
||
|
||
Callers must not cache the same Blob twice. Once a Blob is cached,
|
||
its contents must not be modified, and it must not be freed or re-cached.
|
||
|
||
Arguments:
|
||
|
||
TSvcCache - An initialized TSVC_CACHE structure.
|
||
pszDirectoryName - The name that will be used as a key in the cache.
|
||
iDemultiplexor - Identifies the type of the object to be stored
|
||
pvBlob - Pointer to the actual object to be stored
|
||
bKeepCheckedOut - If TRUE, the caller can keep a reference to the cached object.
|
||
pSecDesc - An optional SECURITY_DESCRIPTOR that goes along with the object
|
||
|
||
Return Values:
|
||
|
||
TRUE - The block successfully added to the cache
|
||
FALSE - The block could not be added to the cache
|
||
|
||
--*/
|
||
{
|
||
BOOL bSuccess;
|
||
PBLOB_HEADER pBlob = (PBLOB_HEADER)pvBlob;
|
||
DBG_ASSERT( TS_BLOB_SIGNATURE == pBlob->Signature );
|
||
|
||
//
|
||
// set up the key
|
||
//
|
||
CBlobKey * pbk = pBlob->pBlobKey;
|
||
DBG_ASSERT( NULL != pbk );
|
||
|
||
pbk->m_cbPathName = cchDirectoryName;
|
||
pbk->m_pszPathName = (PCHAR) ALLOC(pbk->m_cbPathName + 1);
|
||
if (NULL != pbk->m_pszPathName) {
|
||
memcpy(pbk->m_pszPathName, pszDirectoryName, pbk->m_cbPathName + 1);
|
||
} else {
|
||
pbk->m_cbPathName = 0;
|
||
pbk->m_pszPathName = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
IISstrupr( (PUCHAR)pbk->m_pszPathName );
|
||
|
||
pbk->m_dwService = TSvcCache.GetServiceId();
|
||
pbk->m_dwInstance = TSvcCache.GetInstanceId();
|
||
pbk->m_dwDemux = iDemultiplexor;
|
||
|
||
//
|
||
// try to cache
|
||
//
|
||
|
||
bSuccess = CacheBlob(pBlob);
|
||
|
||
if (bSuccess && !bKeepCheckedOut) {
|
||
CheckinBlob(pBlob);
|
||
}
|
||
|
||
if (!bSuccess) {
|
||
FREE(pbk->m_pszPathName);
|
||
pbk->m_pszPathName = NULL;
|
||
pbk->m_cbPathName = 0;
|
||
}
|
||
|
||
return bSuccess;
|
||
} // TsCacheDirectoryBlob
|
||
|
||
|
||
BOOL
|
||
TsDeCacheCachedBlob(
|
||
PVOID pBlobPayload
|
||
)
|
||
/*++
|
||
Description:
|
||
|
||
This function removes a blob payload object from the cache
|
||
|
||
Arguments:
|
||
|
||
pCacheObject - Object to decache
|
||
|
||
Return Values:
|
||
|
||
TRUE on success
|
||
--*/
|
||
{
|
||
DecacheBlob( (PBLOB_HEADER)pBlobPayload );
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
TsCheckOutCachedBlob(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PCSTR pszDirectoryName,
|
||
IN ULONG cchDirectoryName,
|
||
IN ULONG iDemultiplexor,
|
||
IN PVOID * ppvBlob,
|
||
IN HANDLE ,
|
||
IN BOOL ,
|
||
IN PSECURITY_DESCRIPTOR* )
|
||
/*++
|
||
Routine Description:
|
||
|
||
Searches the cache for a named cache entry. If the entry is found,
|
||
it is checked out and returned to the caller.
|
||
|
||
Arguments:
|
||
|
||
TSvcCache - An initialized TSVC_CACHE structure.
|
||
pszDirectoryName - The name used as a key in the cache.
|
||
iDemultiplexor - Identifies the type of the object to be stored
|
||
ppvBlob - If the entry is found, a pointer to it will be
|
||
placed here.
|
||
hAccessToken - Optional parameter used to determine if the
|
||
caller is allowed to access the cached object.
|
||
fMayCacheAccessToken - If this is TRUE, and the caller succesfully gains
|
||
access to the cached object, the hAccessToken will
|
||
be saved with the object in the cache.
|
||
ppSecDesc - If this is non-NULL, the caller will be given a
|
||
copy of the objects security descriptor.
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
CHAR achUpName[MAX_PATH+1];
|
||
BOOL bSuccess;
|
||
|
||
// People really do use this.
|
||
// DBG_ASSERT( ppSecDesc == NULL );
|
||
|
||
//
|
||
// Make sure the path is upper case
|
||
//
|
||
IISstrncpy(achUpName, pszDirectoryName, MAX_PATH);
|
||
achUpName[MAX_PATH] = 0;
|
||
cchDirectoryName = min(cchDirectoryName, MAX_PATH);
|
||
|
||
IISstrupr( reinterpret_cast<PUCHAR>(achUpName) );
|
||
|
||
bSuccess = CheckoutBlob(achUpName,
|
||
cchDirectoryName,
|
||
TSvcCache.GetServiceId(),
|
||
TSvcCache.GetInstanceId(),
|
||
iDemultiplexor,
|
||
(PBLOB_HEADER *) ppvBlob);
|
||
|
||
if (bSuccess) {
|
||
//
|
||
// Security handled by the caller
|
||
//
|
||
((PBLOB_HEADER)*ppvBlob)->TTL = 1;
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
TsCheckInCachedBlob(
|
||
IN PVOID pvBlob
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
When a client is done with a blob it must check it back into the cache.
|
||
|
||
Arguments:
|
||
|
||
pvBlob - The object to be checked in
|
||
|
||
Return Values:
|
||
|
||
TRUE for success
|
||
--*/
|
||
{
|
||
CheckinBlob((PBLOB_HEADER) pvBlob);
|
||
|
||
return( TRUE );
|
||
} // TsCheckInCachedBlob
|
||
|
||
|
||
BOOL
|
||
TsCheckInOrFree(
|
||
IN PVOID pvOldBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks in a cached memory block or
|
||
frees a non-cached memory block allocated with TsAllocate().
|
||
|
||
Arguments:
|
||
|
||
pvOldBlock - The address of the block to free. (Must be
|
||
non-NULL.)
|
||
|
||
Return Value:
|
||
|
||
TRUE - The block was freed. The pointer pvOldBlock is no longer
|
||
valid.
|
||
|
||
FALSE - The block was not freed. Possible reasons include:
|
||
|
||
- pvOldBlock does not point to a block allocated with
|
||
TsAllocate().
|
||
|
||
--*/
|
||
{
|
||
PBLOB_HEADER pBlob = (PBLOB_HEADER) pvOldBlock;
|
||
TSVC_CACHE dummy;
|
||
|
||
if (pBlob->IsCached) {
|
||
CheckinBlob(pBlob);
|
||
} else {
|
||
TsFree(dummy, (PVOID)pBlob);
|
||
}
|
||
return( TRUE );
|
||
} // TsCheckInOrFree
|
||
|
||
|
||
|
||
BOOL
|
||
TsCacheFlushDemux(
|
||
IN ULONG iDemux
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Flush all cache items whose demultiplexor matches that specified.
|
||
|
||
Arguments:
|
||
|
||
iDemux - Value of demux whose cache items are to be flushed.
|
||
|
||
--*/
|
||
{
|
||
if (RESERVED_DEMUX_OPEN_FILE == iDemux) {
|
||
FlushFileCache();
|
||
} else {
|
||
//
|
||
// Only place where this function is called from is from odbc with
|
||
// a demux of RESERVED_DEMUX_QUERY_CACHE. We do not need to worry
|
||
// about other cases
|
||
//
|
||
FlushBlobCache();
|
||
}
|
||
|
||
return TRUE;
|
||
} // TsCacheFlushDemux
|
||
|
||
|
||
BOOL
|
||
FlushFilterService(
|
||
PBLOB_HEADER pBlob,
|
||
PVOID pv
|
||
)
|
||
{
|
||
DWORD dwServerMask = * (DWORD *)pv;
|
||
return (pBlob->pBlobKey->m_dwService == dwServerMask);
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
TsCacheFlush(
|
||
IN DWORD dwServerMask
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function flushes the blob cache of all items for the specified service
|
||
or for all services if dwServerMask is zero.
|
||
|
||
--*/
|
||
{
|
||
if (dwServerMask) {
|
||
FilteredFlushBlobCache(FlushFilterService, &dwServerMask);
|
||
} else {
|
||
FlushBlobCache();
|
||
}
|
||
|
||
return TRUE;
|
||
} // TsCacheFlush
|
||
|
||
|
||
BOOL
|
||
FlushFilterUser(
|
||
TS_OPEN_FILE_INFO *pOpenFile,
|
||
PVOID pv
|
||
)
|
||
{
|
||
HANDLE hUser = * (HANDLE *)pv;
|
||
return (pOpenFile->QueryUser() == hUser);
|
||
}
|
||
|
||
|
||
BOOL
|
||
TsCacheFlushUser(
|
||
IN HANDLE hUserToken,
|
||
IN BOOL fDefer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function flushes all file handles associated the passed user context
|
||
|
||
Arguments:
|
||
|
||
hUserToken - User token to flush from the cache
|
||
fDefer - Build list but close handles later in worker thread (Not supported)
|
||
|
||
--*/
|
||
{
|
||
FilteredFlushFileCache(FlushFilterUser, &hUserToken);
|
||
|
||
return TRUE;
|
||
} // TsCacheFlushUser
|
||
|
||
|
||
typedef struct _FLUSH_URL_PARAM {
|
||
PCSTR pszURL;
|
||
DWORD cbURL;
|
||
DWORD dwService;
|
||
DWORD dwInstance;
|
||
} FLUSH_URL_PARAM;
|
||
|
||
|
||
BOOL
|
||
FlushFilterURL(
|
||
PBLOB_HEADER pBlob,
|
||
PVOID pv
|
||
)
|
||
{
|
||
DBG_ASSERT( pBlob );
|
||
DBG_ASSERT( pBlob->pBlobKey );
|
||
|
||
FLUSH_URL_PARAM * fup = (FLUSH_URL_PARAM *)pv;
|
||
CBlobKey * pbk = pBlob->pBlobKey;
|
||
BOOL bAtRoot;
|
||
|
||
//
|
||
// If we're flushing everything, then don't bother
|
||
// with the string comparison
|
||
//
|
||
bAtRoot = (fup->cbURL == 1) && (fup->pszURL[0] == '/');
|
||
|
||
//
|
||
// If the service, instance, and URL prefixes match then we flush.
|
||
//
|
||
return ( (pbk->m_dwService == fup->dwService)
|
||
&& (pbk->m_dwInstance == fup->dwInstance)
|
||
&& (bAtRoot
|
||
|| ((pbk->m_cbPathName >= fup->cbURL)
|
||
&& (memcmp(pbk->m_pszPathName, fup->pszURL, fup->cbURL) == 0))) );
|
||
}
|
||
|
||
|
||
VOID
|
||
TsFlushURL(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PCSTR pszURL,
|
||
IN DWORD dwURLLength,
|
||
IN ULONG iDemultiplexor
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input a URL and removes from the cache all cached
|
||
objects that have the input URL as their prefix. This is mostly called
|
||
when we get a change notify for metadata.
|
||
|
||
Arguments
|
||
|
||
TSvcCache - Service cache
|
||
pszURL - The URL prefix to be flushed.
|
||
iDemultiplexor - The demultiplexor for the caller's entries.
|
||
|
||
Returns
|
||
|
||
Nothing
|
||
|
||
--*/
|
||
{
|
||
FLUSH_URL_PARAM fuparam;
|
||
CHAR achUpName[MAX_PATH+1];
|
||
|
||
//
|
||
// It really only makes sense to flush the URI cache
|
||
// with this function.
|
||
//
|
||
DBG_ASSERT( RESERVED_DEMUX_URI_INFO == iDemultiplexor );
|
||
|
||
DBG_ASSERT( MAX_PATH >= dwURLLength );
|
||
|
||
//
|
||
// Make sure the path is upper case
|
||
//
|
||
IISstrncpy(achUpName, pszURL, MAX_PATH);
|
||
achUpName[dwURLLength] = 0;
|
||
|
||
IISstrupr( (PUCHAR) achUpName );
|
||
|
||
fuparam.pszURL = achUpName;
|
||
fuparam.cbURL = dwURLLength;
|
||
fuparam.dwService = TSvcCache.GetServiceId();
|
||
fuparam.dwInstance = TSvcCache.GetInstanceId();
|
||
|
||
FilteredFlushURIBlobCache(FlushFilterURL, &fuparam);
|
||
}
|
||
|
||
|
||
BOOL
|
||
TsExpireCachedBlob(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PVOID pvBlob
|
||
)
|
||
{
|
||
DecacheBlob((PBLOB_HEADER) pvBlob);
|
||
|
||
return TRUE;
|
||
} // TsExpireCachedBlob
|
||
|
||
//
|
||
// Misc cache management
|
||
//
|
||
|
||
|
||
BOOL
|
||
TsCacheQueryStatistics(
|
||
IN DWORD Level,
|
||
IN DWORD dwServerMask,
|
||
IN INETA_CACHE_STATISTICS * pCacheCtrs
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the statistics for the global cache or for the
|
||
individual services
|
||
|
||
Arguments:
|
||
|
||
Level - Only valid value is 0
|
||
dwServerMask - Server mask to retrieve statistics for or 0 for the sum
|
||
of the services
|
||
pCacheCtrs - Receives the statistics for cache
|
||
|
||
Notes:
|
||
CacheBytesTotal and CacheBytesInUse are not kept on a per-server basis
|
||
so they are only returned when retrieving summary statistics.
|
||
|
||
Returns:
|
||
|
||
TRUE on success, FALSE on failure
|
||
--*/
|
||
{
|
||
if ( dwServerMask > LAST_PERF_CTR_SVC )
|
||
{
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return FALSE;
|
||
}
|
||
|
||
if ( g_pFileCacheStats
|
||
&& g_pURICacheStats
|
||
&& g_pBlobCacheStats
|
||
&& (dwServerMask == 0) ) {
|
||
|
||
pCacheCtrs->FilesCached = g_pFileCacheStats->GetFilesCached();
|
||
pCacheCtrs->TotalFilesCached = g_pFileCacheStats->GetTotalFilesCached();
|
||
pCacheCtrs->FileHits = g_pFileCacheStats->GetHits();
|
||
pCacheCtrs->FileMisses = g_pFileCacheStats->GetMisses();
|
||
pCacheCtrs->FileFlushes = g_pFileCacheStats->GetFlushes();
|
||
pCacheCtrs->FlushedEntries = g_pFileCacheStats->GetFlushedEntries();
|
||
pCacheCtrs->TotalFlushed = g_pFileCacheStats->GetTotalFlushed();
|
||
|
||
pCacheCtrs->URICached = g_pURICacheStats->GetBlobsCached();
|
||
pCacheCtrs->TotalURICached = g_pURICacheStats->GetTotalBlobsCached();
|
||
pCacheCtrs->URIHits = g_pURICacheStats->GetHits();
|
||
pCacheCtrs->URIMisses = g_pURICacheStats->GetMisses();
|
||
pCacheCtrs->URIFlushes = g_pURICacheStats->GetFlushes();
|
||
pCacheCtrs->TotalURIFlushed = g_pURICacheStats->GetTotalFlushed();
|
||
|
||
pCacheCtrs->BlobCached = g_pBlobCacheStats->GetBlobsCached();
|
||
pCacheCtrs->TotalBlobCached = g_pBlobCacheStats->GetTotalBlobsCached();
|
||
pCacheCtrs->BlobHits = g_pBlobCacheStats->GetHits();
|
||
pCacheCtrs->BlobMisses = g_pBlobCacheStats->GetMisses();
|
||
pCacheCtrs->BlobFlushes = g_pBlobCacheStats->GetFlushes();
|
||
pCacheCtrs->TotalBlobFlushed = g_pBlobCacheStats->GetTotalFlushed();
|
||
|
||
QueryMemoryCacheStatistics( pCacheCtrs, FALSE );
|
||
|
||
} else {
|
||
//
|
||
// Either we're reporting for a specific service
|
||
// or stats are not set up. Set all cache
|
||
// counters to zero.
|
||
//
|
||
pCacheCtrs->FilesCached = 0;
|
||
pCacheCtrs->TotalFilesCached = 0;
|
||
pCacheCtrs->FileHits = 0;
|
||
pCacheCtrs->FileMisses = 0;
|
||
pCacheCtrs->FileFlushes = 0;
|
||
pCacheCtrs->FlushedEntries = 0;
|
||
pCacheCtrs->TotalFlushed = 0;
|
||
|
||
pCacheCtrs->URICached = 0;
|
||
pCacheCtrs->TotalURICached = 0;
|
||
pCacheCtrs->URIHits = 0;
|
||
pCacheCtrs->URIMisses = 0;
|
||
pCacheCtrs->URIFlushes = 0;
|
||
pCacheCtrs->TotalURIFlushed = 0;
|
||
|
||
pCacheCtrs->BlobCached = 0;
|
||
pCacheCtrs->TotalBlobCached = 0;
|
||
pCacheCtrs->BlobHits = 0;
|
||
pCacheCtrs->BlobMisses = 0;
|
||
pCacheCtrs->BlobFlushes = 0;
|
||
pCacheCtrs->TotalBlobFlushed = 0;
|
||
|
||
QueryMemoryCacheStatistics( pCacheCtrs, TRUE );
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
TsCacheClearStatistics(
|
||
IN DWORD dwServerMask
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Clears the the specified service's statistics
|
||
|
||
--*/
|
||
{
|
||
if ( dwServerMask > LAST_PERF_CTR_SVC )
|
||
{
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Currently this function isn't supported
|
||
//
|
||
|
||
SetLastError( ERROR_NOT_SUPPORTED );
|
||
return FALSE;
|
||
} // TsCacheClearStatistics
|
||
|
||
|
||
|
||
const char * g_IISAuxCounterNames[] =
|
||
{
|
||
"Aac Open URI Files",
|
||
"Cac Calls to TsOpenURI()",
|
||
"Cac Calls to TsCloseURI()",
|
||
"Max Counters"
|
||
};
|
||
|
||
|
||
|
||
extern "C"
|
||
VOID
|
||
TsDumpCacheCounters( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
|
||
{
|
||
DWORD cb = 0;
|
||
|
||
*lpcbBuffer = cb;
|
||
return ;
|
||
} // TsDumpCacheCounters()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TsDumpHashTableStats( IN OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
|
||
{
|
||
CLKRHashTableStats hts;
|
||
|
||
if (!g_pFileInfoTable) {
|
||
*lpcbBuffer = 0;
|
||
return;
|
||
}
|
||
|
||
hts = g_pFileInfoTable->GetStatistics();
|
||
|
||
*lpcbBuffer = sprintf( pchBuffer,
|
||
"<TABLE>"
|
||
"<TR><TD>Record Count</TD><TD>%d</TD></TR>"
|
||
"<TR><TD>Table Size</TD><TD>%d</TD></TR>"
|
||
"<TR><TD>Directory Size</TD><TD>%d</TD></TR>"
|
||
"<TR><TD>Longest Chain</TD><TD>%d</TD></TR>"
|
||
"<TR><TD>Empty Slots</TD><TD>%d</TD></TR>"
|
||
"<TR><TD>Split Factor</TD><TD>%f</TD></TR>"
|
||
"<TR><TD>Average Search Length</TD><TD>%f</TD></TR>"
|
||
"<TR><TD>Expected Search Length</TD><TD>%f</TD></TR>"
|
||
"<TR><TD>Average Unsuccessful Search Length</TD><TD>%f</TD></TR>"
|
||
"<TR><TD>Expected Unsuccessful Search Length</TD><TD>%f</TD></TR>"
|
||
"</TABLE>",
|
||
hts.RecordCount,
|
||
hts.TableSize,
|
||
hts.DirectorySize,
|
||
hts.LongestChain,
|
||
hts.EmptySlots,
|
||
hts.SplitFactor,
|
||
hts.AvgSearchLength,
|
||
hts.ExpSearchLength,
|
||
hts.AvgUSearchLength,
|
||
hts.ExpUSearchLength );
|
||
|
||
}
|
||
|
||
VOID
|
||
TsDumpCacheToHtml( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
|
||
{
|
||
LIST_ENTRY * pEntry;
|
||
DWORD cItemsOnBin = 0;
|
||
DWORD cTotalItems = 0;
|
||
DWORD i, c, cb;
|
||
DWORD cbTable;
|
||
|
||
cb = wsprintf( pchBuffer,
|
||
" <h4>File Hash Table Stats</h4> " );
|
||
|
||
TsDumpHashTableStats( pchBuffer + cb, &cbTable );
|
||
cb += cbTable;
|
||
|
||
cb += wsprintf( pchBuffer + cb,
|
||
" <h4>Some other stats</h4> ");
|
||
|
||
if (g_pFileCacheStats) {
|
||
g_pFileCacheStats->DumpToHtml(pchBuffer + cb, &cbTable);
|
||
cb += cbTable;
|
||
}
|
||
|
||
DumpMemoryCacheToHtml( pchBuffer + cb, &cbTable );
|
||
cb += cbTable;
|
||
|
||
*lpcbBuffer = cb;
|
||
|
||
return;
|
||
} // TsDumpCacheToHtml()
|
||
|
||
|
||
//
|
||
// tsunami.cxx
|
||
//
|
||
|