708 lines
17 KiB
C++
708 lines
17 KiB
C++
/*++
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|