windows-nt/Source/XPSP1/NT/admin/services/drizzle/server/isapi/config.cpp

708 lines
17 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
config.cpp
Abstract:
This file implements metabase access routines for virtual directories.
--*/
#include "precomp.h"
VDirConfig::VDirConfig(
StringHandle Path,
IMSAdminBase *AdminBase
) :
m_Refs(1)
{
//
// Read in all the metabase configuration for the virtual directory.
//
HRESULT Hr;
METADATA_HANDLE MdVDirKey = NULL;
GetSystemTimeAsFileTime( &m_LastLookup );
try
{
m_Path = Path;
StringHandleW UnicodePath = Path;
Hr = AdminBase->OpenKey(
METADATA_MASTER_ROOT_HANDLE,
(const WCHAR*)UnicodePath,
METADATA_PERMISSION_READ,
30000,
&MdVDirKey );
if ( FAILED(Hr) )
throw ServerException( Hr );
m_PhysicalPath =
GetMetaDataString(
AdminBase,
MdVDirKey,
NULL,
MD_VR_PATH,
"" );
DWORD UploadEnabled =
GetMetaDataDWORD(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED ),
0);
m_UploadEnabled = UploadEnabled ? true : false;
m_ConnectionsDir =
GetMetaDataString(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_CONNECTION_DIR ),
MD_DEFAULT_BITS_CONNECTION_DIRA );
m_NoProgressTimeout =
GetMetaDataDWORD(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_NO_PROGRESS_TIMEOUT ),
MD_DEFAULT_NO_PROGESS_TIMEOUT );
StringHandle MaxFilesizeString =
GetMetaDataString(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_MAX_FILESIZE ),
MD_DEFAULT_BITS_MAX_FILESIZEA );
if ( MaxFilesizeString.Size() == 0 )
{
m_MaxFileSize = 0xFFFFFFFFFFFFFFFF;
}
else
{
UINT64 MaxFileSize;
int ScanRet = sscanf( (const char*)MaxFilesizeString, "%I64u", &MaxFileSize );
if ( 1 != ScanRet )
throw ServerException( E_INVALIDARG );
m_MaxFileSize = MaxFileSize;
}
DWORD NotificationType =
GetMetaDataDWORD(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_NOTIFICATION_URL_TYPE ),
MD_DEFAULT_BITS_NOTIFICATION_URL_TYPE );
if ( NotificationType > BITS_NOTIFICATION_TYPE_MAX )
throw ServerException( E_INVALIDARG );
m_NotificationType = (BITS_SERVER_NOTIFICATION_TYPE)NotificationType;
m_NotificationURL =
GetMetaDataString(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_NOTIFICATION_URL ),
MD_DEFAULT_BITS_NOTIFICATION_URLA );
m_HostId =
GetMetaDataString(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_HOSTID ),
MD_DEFAULT_BITS_HOSTIDA );
m_HostIdFallbackTimeout =
GetMetaDataDWORD(
AdminBase,
MdVDirKey,
NULL,
g_PropertyMan->GetPropertyMetabaseID( MD_BITS_HOSTID_FALLBACK_TIMEOUT ),
MD_DEFAULT_HOSTID_FALLBACK_TIMEOUT );
m_ExecutePermissions =
GetMetaDataDWORD(
AdminBase,
MdVDirKey,
NULL,
MD_ACCESS_PERM,
MD_ACCESS_READ );
AdminBase->CloseKey( MdVDirKey );
}
catch( ServerException Exception )
{
if ( MdVDirKey )
AdminBase->CloseKey( MdVDirKey );
throw;
}
}
ConfigurationManager::ConfigurationManager()
{
m_IISAdminBase = NULL;
bool CSInitialize = false;
memset( m_PathCacheEntries, 0, sizeof( m_PathCacheEntries ) );
memset( m_MapCacheEntries, 0, sizeof( m_MapCacheEntries ) );
HRESULT Hr =
CoInitializeEx( NULL, COINIT_MULTITHREADED );
if ( FAILED(Hr) )
throw ServerException( Hr );
try
{
if ( !InitializeCriticalSectionAndSpinCount( &m_CacheCS, 0x80000100 ) )
throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );
CSInitialize = true;
Hr =
CoCreateInstance(
GETAdminBaseCLSID(TRUE),
NULL,
CLSCTX_SERVER,
__uuidof( IMSAdminBase ),
(LPVOID*)&m_IISAdminBase );
if ( FAILED(Hr) )
throw ServerException( Hr );
Hr = m_IISAdminBase->GetSystemChangeNumber( &m_ChangeNumber );
if ( FAILED(Hr))
throw ServerException( Hr );
CoUninitialize();
}
catch( ServerException )
{
if ( m_IISAdminBase )
m_IISAdminBase->Release();
if ( CSInitialize )
DeleteCriticalSection( &m_CacheCS );
CoUninitialize();
throw;
}
}
ConfigurationManager::~ConfigurationManager()
{
FlushCache();
DeleteCriticalSection( &m_CacheCS );
if ( m_IISAdminBase )
m_IISAdminBase->Release();
}
void
ConfigurationManager::FlushCache()
{
for( unsigned int i = 0; i < PATH_CACHE_ENTRIES; i++ )
{
if ( m_PathCacheEntries[i] )
{
m_PathCacheEntries[i]->Release();
m_PathCacheEntries[i] = NULL;
}
}
for( unsigned int i = 0; i < MAP_CACHE_ENTRIES; i++ )
{
delete m_MapCacheEntries[i];
m_MapCacheEntries[i] = NULL;
}
}
VDirConfig*
ConfigurationManager::Lookup(
StringHandle Path )
{
VDirConfig* ReturnVal = NULL;
for( unsigned int i=0; i < PATH_CACHE_ENTRIES; i++ )
{
if ( m_PathCacheEntries[i] )
{
if ( strcmp( (const char*)m_PathCacheEntries[i]->m_Path, (const char*)Path) == 0 )
{
ReturnVal = m_PathCacheEntries[i];
GetSystemTimeAsFileTime( &ReturnVal->m_LastLookup );
ReturnVal->AddRef();
}
}
}
return ReturnVal;
}
void
ConfigurationManager::Insert(
VDirConfig *NewConfig )
{
//
// Insert a new virtual directory configuration into the
// virtual directory cache. Expire an old entry if needed.
//
int BestSlot = 0;
FILETIME WorstTime;
memset( &WorstTime, 0xFF, sizeof( WorstTime ) );
for( unsigned int i=0; i < PATH_CACHE_ENTRIES; i++ )
{
if ( !m_PathCacheEntries[i] )
{
BestSlot = i;
break;
}
else if ( CompareFileTime( &m_PathCacheEntries[i]->m_LastLookup, &WorstTime ) < 0 )
{
WorstTime = m_PathCacheEntries[i]->m_LastLookup;
BestSlot = i;
}
}
if ( m_PathCacheEntries[BestSlot] )
m_PathCacheEntries[BestSlot]->Release();
NewConfig->AddRef();
m_PathCacheEntries[BestSlot] = NewConfig;
}
VDirConfig*
ConfigurationManager::Lookup(
StringHandle InstanceMetabasePath,
StringHandle URL )
{
//
// Find the virtual directories configuration in the cache.
//
VDirConfig* ReturnVal = NULL;
for( unsigned int i=0; i < MAP_CACHE_ENTRIES; i++ )
{
MapCacheEntry* CacheEntry = m_MapCacheEntries[i];
if ( CacheEntry )
{
if ( ( strcmp( (const char*)CacheEntry->m_InstanceMetabasePath,
(const char*)InstanceMetabasePath) == 0 ) &&
( strcmp( (const char*)CacheEntry->m_URL,
(const char*)URL ) == 0 ) )
{
GetSystemTimeAsFileTime( &CacheEntry->m_LastLookup );
ReturnVal = m_MapCacheEntries[i]->m_Config;
ReturnVal->AddRef();
return ReturnVal;
}
}
}
return ReturnVal;
}
VDirConfig*
ConfigurationManager::GetVDirConfig(
StringHandle Path )
{
VDirConfig* Config = Lookup( Path );
if ( !Config )
{
try
{
Config = new VDirConfig( Path, m_IISAdminBase );
Insert( Config );
}
catch( ServerException Exception )
{
if ( Config )
Config->Release();
throw;
}
}
return Config;
}
VDirConfig*
ConfigurationManager::Insert(
StringHandle InstanceMetabasePath,
StringHandle URL,
StringHandle Path )
{
VDirConfig* Config = GetVDirConfig( Path );
try
{
MapCacheEntry* CacheEntry =
new MapCacheEntry(
InstanceMetabasePath,
URL,
Config );
int BestSlot = 0;
FILETIME WorstTime;
memset( &WorstTime, 0xFF, sizeof( WorstTime ) );
for( unsigned int i=0; i < MAP_CACHE_ENTRIES; i++ )
{
if ( !m_MapCacheEntries[i] )
{
BestSlot = i;
break;
}
else if ( CompareFileTime( &m_MapCacheEntries[i]->m_LastLookup, &WorstTime ) < 0 )
{
WorstTime = m_MapCacheEntries[i]->m_LastLookup;
BestSlot = i;
}
}
if ( m_MapCacheEntries[BestSlot] )
delete m_MapCacheEntries[BestSlot];
m_MapCacheEntries[BestSlot] = CacheEntry;
return Config;
}
catch( ServerException Exception )
{
Config->Release();
throw;
}
}
StringHandle
ConfigurationManager::GetVDirPath(
StringHandle InstanceMetabasePath,
StringHandle URL )
{
//
// Find the virtual directory that coresponds to the URL.
// Do this by matching the URL up with the metabase keys. Keep
// pruning off the URL untill the longest metabase path is found
// that is a virtual directory.
//
StringHandleW InstanceMetabasePathW = InstanceMetabasePath;
StringHandleW URLW = URL;
WCHAR *Path = NULL;
METADATA_HANDLE MdVDirKey = NULL;
try
{
WCHAR *PathEnd = NULL;
WCHAR *CurrentEnd = NULL;
WCHAR RootString[] = L"/Root";
SIZE_T InstancePathSize = InstanceMetabasePathW.Size();
SIZE_T URLSize = URLW.Size();
SIZE_T RootStringSize = ( sizeof( RootString ) / sizeof( *RootString ) ) - 1;
Path = new WCHAR[ InstancePathSize + URLSize + RootStringSize + 1 ];
memcpy( Path, (const WCHAR*)InstanceMetabasePathW, InstancePathSize * sizeof( WCHAR ) );
PathEnd = Path + InstancePathSize;
memcpy( PathEnd, RootString, RootStringSize * sizeof( WCHAR ) );
memcpy( PathEnd + RootStringSize, (const WCHAR*)URLW, ( URLSize + 1 )* sizeof( WCHAR ) );
CurrentEnd = PathEnd + RootStringSize + URLSize;
while( 1 )
{
HRESULT Hr =
m_IISAdminBase->OpenKey(
METADATA_MASTER_ROOT_HANDLE, //metabase handle.
Path, //path to the key, relative to hMDHandle.
METADATA_PERMISSION_READ, //specifies read and write permissions.
5000, //the time, in milliseconds, before the method times out.
&MdVDirKey //receives the handle to the opened key.
);
if ( SUCCEEDED( Hr ) )
{
//
// Check if this is a virtual directory
//
WCHAR NodeName[ 255 ];
DWORD RequiredDataLen;
METADATA_RECORD MDRecord;
MDRecord.dwMDIdentifier = MD_KEY_TYPE;
MDRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
MDRecord.dwMDUserType = IIS_MD_UT_SERVER;
MDRecord.dwMDDataType = STRING_METADATA;
MDRecord.dwMDDataLen = sizeof( NodeName );
MDRecord.pbMDData = (unsigned char*)NodeName;
MDRecord.dwMDDataTag = 0;
Hr = m_IISAdminBase->GetData(
MdVDirKey,
NULL,
&MDRecord,
&RequiredDataLen );
if ( FAILED(Hr) && ( Hr != MD_ERROR_DATA_NOT_FOUND ) &&
( Hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) ) )
throw ServerException( Hr );
if ( SUCCEEDED( Hr ) && wcscmp( L"IIsWebVirtualDir", NodeName ) == 0 )
{
// Found the path, so return the data
StringHandle VDirPath = Path;
delete[] Path;
m_IISAdminBase->CloseKey( MdVDirKey );
return VDirPath;
}
}
else if ( Hr != HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) )
{
throw ServerException( Hr );
}
//
// If this is the end of the URL, then nothing else can be done
//
if ( CurrentEnd == PathEnd )
throw ServerException( E_INVALIDARG );
m_IISAdminBase->CloseKey( MdVDirKey );
MdVDirKey = NULL;
// Chop off the rightmost subpart
while( CurrentEnd != PathEnd && *CurrentEnd != L'/' &&
*CurrentEnd != L'\\' )
CurrentEnd--;
if ( *CurrentEnd == L'/' || *CurrentEnd == L'\\' )
*CurrentEnd = L'\0';
// Attempt another round
}
}
catch( ServerException Exception )
{
delete[] Path;
if ( MdVDirKey )
m_IISAdminBase->CloseKey( MdVDirKey );
throw;
}
}
bool
ConfigurationManager::HandleCacheConsistency()
{
//
// Handle cache consistency. This is done my calling IIS to check the change number.
// If the current change number is different then the change number for the last lookup,
// then flush the cache.
//
DWORD ChangeNumber;
HRESULT Hr = m_IISAdminBase->GetSystemChangeNumber( &ChangeNumber );
if ( FAILED(Hr) )
{
throw ServerException( Hr );
}
if ( ChangeNumber == m_ChangeNumber )
return true; // cache is consistent
FlushCache();
m_ChangeNumber = ChangeNumber;
return false; // cache was flushed.
}
VDirConfig*
ConfigurationManager::GetConfig(
StringHandle InstanceMetabasePath,
StringHandle URL )
{
//
// Toplevel function to do everything to lookup the configuration to use for an URL.
//
METADATA_HANDLE MdVDirKey = NULL;
VDirConfig * Config = NULL;
HRESULT Hr =
CoInitializeEx( NULL, COINIT_MULTITHREADED );
if ( FAILED(Hr) )
throw ServerException( Hr );
HANDLE ImpersonationToken = NULL;
bool DidRevertToSelf = false;
try
{
EnterCriticalSection( &m_CacheCS );
if ( HandleCacheConsistency() )
{
// The cache was consistent. Chances are good
// that the lookup will succeed
Config = Lookup( InstanceMetabasePath, URL );
if ( Config )
{
CoUninitialize();
LeaveCriticalSection( &m_CacheCS );
return Config;
}
}
// Need to revert to the system process
// to address the metabase
if ( !OpenThreadToken(
GetCurrentThread(),
TOKEN_ALL_ACCESS,
TRUE,
&ImpersonationToken ) )
{
DWORD dwError = GetLastError();
if (dwError != ERROR_NO_TOKEN)
throw ServerException( HRESULT_FROM_WIN32( dwError ) );
}
else
{
if ( !RevertToSelf() )
throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );
DidRevertToSelf = true;
}
StringHandle Path = GetVDirPath( InstanceMetabasePath, URL );
Config = Insert( InstanceMetabasePath, URL, Path );
if ( DidRevertToSelf )
{
BITSSetCurrentThreadToken( ImpersonationToken );
}
if ( ImpersonationToken )
CloseHandle( ImpersonationToken );
CoUninitialize();
LeaveCriticalSection( &m_CacheCS );
return Config;
}
catch( ServerException Exception )
{
if ( Config )
delete Config;
if ( MdVDirKey )
m_IISAdminBase->CloseKey( MdVDirKey );
if ( DidRevertToSelf )
BITSSetCurrentThreadToken( ImpersonationToken );
if ( ImpersonationToken )
CloseHandle( ImpersonationToken );
CoUninitialize();
LeaveCriticalSection( &m_CacheCS );
throw;
}
}