|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
config.cpp
Abstract:
This file implements metabase access routines for virtual directories.
--*/
#include "precomp.h"
StringHandle VDirConfig::GetFullPath( StringHandle Path ) { StringHandle ParsedPath; CHAR *FilePart = NULL; DWORD BufferLength = MAX_PATH;
while( 1 ) { CHAR *PathBuffer = ParsedPath.AllocBuffer( BufferLength );
DWORD Result = GetFullPathName( Path, BufferLength, PathBuffer, &FilePart );
if ( Result > BufferLength ) { BufferLength = Result; continue; }
if ( !Result ) { Log( LOG_ERROR, "Unable to get the full path name for %s, error 0x%8.8X", (const char*)Path, HRESULT_FROM_WIN32( GetLastError() ) ); throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) ); }
ParsedPath.SetStringSize(); break;
}
return ParsedPath; }
VDirConfig::VDirConfig( StringHandle Path, SmartMetabasePointer 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, METABASE_OPEN_KEY_TIMEOUT, &MdVDirKey );
if ( FAILED(Hr) ) throw ServerException( Hr );
m_PhysicalPath = GetMetaDataString( AdminBase.Get(), MdVDirKey, NULL, MD_VR_PATH, "" );
m_PhysicalPath = GetFullPath( m_PhysicalPath );
DWORD UploadEnabled = GetMetaDataDWORD( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED ), 0);
m_UploadEnabled = UploadEnabled ? true : false;
m_ConnectionsDir = GetMetaDataString( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_CONNECTION_DIR ), MD_DEFAULT_BITS_CONNECTION_DIRA ); m_SessionDir = m_PhysicalPath + StringHandle( "\\" ) + m_ConnectionsDir; m_RequestsDir = m_SessionDir + StringHandle( "\\" ) + StringHandle( REQUESTS_DIR_NAME ); m_RepliesDir = m_SessionDir + StringHandle( "\\" ) + StringHandle( REPLIES_DIR_NAME );
m_NoProgressTimeout = GetMetaDataDWORD( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_NO_PROGRESS_TIMEOUT ), MD_DEFAULT_NO_PROGESS_TIMEOUT );
StringHandle MaxFilesizeString = GetMetaDataString( AdminBase.Get(), 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.Get(), 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.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_NOTIFICATION_URL ), MD_DEFAULT_BITS_NOTIFICATION_URLA );
m_HostId = GetMetaDataString( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_HOSTID ), MD_DEFAULT_BITS_HOSTIDA );
m_HostIdFallbackTimeout = GetMetaDataDWORD( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_HOSTID_FALLBACK_TIMEOUT ), MD_DEFAULT_HOSTID_FALLBACK_TIMEOUT );
m_ExecutePermissions = GetMetaDataDWORD( AdminBase.Get(), MdVDirKey, NULL, MD_ACCESS_PERM, MD_ACCESS_READ );
#if defined( ALLOW_OVERWRITES )
m_AllowOverwrites = !!GetMetaDataDWORD( AdminBase.Get(), MdVDirKey, NULL, g_PropertyMan->GetPropertyMetabaseID( MD_BITS_ALLOW_OVERWRITES ), MD_DEFAULT_BITS_ALLOW_OVERWRITES );
#else
m_AllowOverwrites = false;
#endif
AdminBase->CloseKey( MdVDirKey );
} catch( const ComError & ) { if ( MdVDirKey ) AdminBase->CloseKey( MdVDirKey ); throw; }
}
ConfigurationManager::ConfigurationManager() {
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.GetRecvPointer() );
if ( FAILED(Hr) ) throw ServerException( Hr );
Hr = m_IISAdminBase->GetSystemChangeNumber( &m_ChangeNumber );
if ( FAILED(Hr)) throw ServerException( Hr );
CoUninitialize(); } catch( const ComError & ) { if ( CSInitialize ) DeleteCriticalSection( &m_CacheCS ); CoUninitialize(); throw; }
}
ConfigurationManager::~ConfigurationManager() { FlushCache(); DeleteCriticalSection( &m_CacheCS );
}
void ConfigurationManager::FlushCache() {
for( unsigned int i = 0; i < PATH_CACHE_ENTRIES; i++ ) { if ( m_PathCacheEntries[i] ) m_PathCacheEntries[i].Clear(); } for( unsigned int i = 0; i < MAP_CACHE_ENTRIES; i++ ) { delete m_MapCacheEntries[i]; m_MapCacheEntries[i] = NULL; }
}
SmartVDirConfig ConfigurationManager::Lookup( StringHandle Path ) {
for( unsigned int i=0; i < PATH_CACHE_ENTRIES; i++ ) {
if ( m_PathCacheEntries[i] ) {
if ( _stricmp( (const char*)m_PathCacheEntries[i]->m_Path, (const char*)Path) == 0 ) { SmartVDirConfig ReturnVal = m_PathCacheEntries[i]; GetSystemTimeAsFileTime( &ReturnVal->m_LastLookup ); return ReturnVal; } } }
return SmartVDirConfig(); }
void ConfigurationManager::Insert( SmartVDirConfig 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; }
}
m_PathCacheEntries[BestSlot] = NewConfig;
}
SmartVDirConfig ConfigurationManager::Lookup( StringHandle InstanceMetabasePath, StringHandle URL, DWORD *pURLDepth ) {
//
// Find the virtual directories configuration in the cache.
//
for( unsigned int i=0; i < MAP_CACHE_ENTRIES; i++ ) {
MapCacheEntry* CacheEntry = m_MapCacheEntries[i];
if ( CacheEntry ) {
if ( ( _stricmp( (const char*)CacheEntry->m_InstanceMetabasePath, (const char*)InstanceMetabasePath) == 0 ) && ( _stricmp( (const char*)CacheEntry->m_URL, (const char*)URL ) == 0 ) ) {
GetSystemTimeAsFileTime( &CacheEntry->m_LastLookup ); *pURLDepth = CacheEntry->m_URLDepth; return CacheEntry->m_Config; } } }
return SmartVDirConfig();
}
SmartVDirConfig ConfigurationManager::GetVDirConfig( StringHandle Path ) {
SmartVDirConfig Config = Lookup( Path );
if ( !Config.Get() ) { *Config.GetRecvPointer() = new VDirConfig( Path, m_IISAdminBase ); Insert( Config ); }
return Config;
}
SmartVDirConfig ConfigurationManager::Insert( StringHandle InstanceMetabasePath, StringHandle URL, StringHandle Path, DWORD URLDepth ) {
SmartVDirConfig Config = GetVDirConfig( Path );
MapCacheEntry* CacheEntry = new MapCacheEntry( InstanceMetabasePath, URL, Config, URLDepth );
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;
}
StringHandle ConfigurationManager::GetVDirPath( StringHandle InstanceMetabasePath, StringHandle URL, DWORD *pURLDepth ) {
//
// 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; *pURLDepth = 0;
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.
METABASE_OPEN_KEY_TIMEOUT, //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';
(*pURLDepth)++;
// Attempt another round
}
} catch( const ComError & ) { 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.
}
SmartVDirConfig ConfigurationManager::GetConfig2( StringHandle InstanceMetabasePath, StringHandle URL, DWORD * pURLDepth ) {
//
// Toplevel function to do everything to lookup the configuration to use for an URL.
//
METADATA_HANDLE MdVDirKey = NULL; SmartVDirConfig Config;
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, pURLDepth );
if ( Config.Get() ) { LeaveCriticalSection( &m_CacheCS ); return Config; }
}
StringHandle Path = GetVDirPath( InstanceMetabasePath, URL, pURLDepth );
Config = Insert( InstanceMetabasePath, URL, Path, *pURLDepth );
LeaveCriticalSection( &m_CacheCS ); return Config;
} catch( const ComError & ) { if ( MdVDirKey ) m_IISAdminBase->CloseKey( MdVDirKey );
LeaveCriticalSection( &m_CacheCS );
throw; } }
SmartVDirConfig ConfigurationManager::GetConfig( StringHandle InstanceMetabasePath, StringHandle URL, DWORD * pURLDepth ) {
bool DidRevertToSelf = false; bool ComInitialized = false;
HANDLE ImpersonationToken = NULL; SmartVDirConfig ReturnVal;
HRESULT Hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if ( FAILED(Hr) ) throw ServerException( Hr );
ComInitialized = true;
try {
// 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; }
ReturnVal = GetConfig2( InstanceMetabasePath, URL, pURLDepth );
if ( DidRevertToSelf ) { BITSSetCurrentThreadToken( ImpersonationToken ); }
if ( ImpersonationToken ) CloseHandle( ImpersonationToken );
CoUninitialize();
return ReturnVal;
} catch( ComError & ) { if ( DidRevertToSelf ) BITSSetCurrentThreadToken( ImpersonationToken );
if ( ImpersonationToken ) CloseHandle( ImpersonationToken );
throw; }
}
|